inspec-core 6.6.0 → 6.8.1

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +22 -22
  3. data/etc/features.sig +6 -6
  4. data/etc/features.yaml +3 -0
  5. data/inspec-core.gemspec +10 -3
  6. data/lib/inspec/base_cli.rb +1 -1
  7. data/lib/inspec/cli.rb +1 -1
  8. data/lib/inspec/config.rb +9 -0
  9. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  10. data/lib/inspec/dsl.rb +1 -1
  11. data/lib/inspec/feature/runner.rb +4 -1
  12. data/lib/inspec/feature.rb +8 -0
  13. data/lib/inspec/fetcher/url.rb +29 -7
  14. data/lib/inspec/iaf_file.rb +3 -2
  15. data/lib/inspec/input_registry.rb +5 -1
  16. data/lib/inspec/profile.rb +2 -2
  17. data/lib/inspec/reporters/cli.rb +1 -1
  18. data/lib/inspec/resources/nftables.rb +14 -1
  19. data/lib/inspec/resources/oracledb_session.rb +12 -3
  20. data/lib/inspec/resources/ssh_config.rb +100 -9
  21. data/lib/inspec/resources/ssh_key.rb +124 -0
  22. data/lib/inspec/resources/sshd_active_config.rb +2 -0
  23. data/lib/inspec/resources/sybase_session.rb +11 -2
  24. data/lib/inspec/resources/virtualization.rb +1 -1
  25. data/lib/inspec/resources.rb +1 -0
  26. data/lib/inspec/rule.rb +15 -10
  27. data/lib/inspec/runner.rb +10 -2
  28. data/lib/inspec/utils/profile_ast_helpers.rb +1 -2
  29. data/lib/inspec/utils/telemetry/base.rb +149 -0
  30. data/lib/inspec/utils/telemetry/http.rb +40 -0
  31. data/lib/inspec/utils/telemetry/null.rb +11 -0
  32. data/lib/inspec/utils/telemetry/run_context_probe.rb +13 -1
  33. data/lib/inspec/utils/telemetry.rb +74 -3
  34. data/lib/inspec/utils/waivers/csv_file_reader.rb +1 -1
  35. data/lib/inspec/utils/waivers/excel_file_reader.rb +1 -1
  36. data/lib/inspec/version.rb +1 -1
  37. data/lib/inspec.rb +0 -1
  38. data/lib/matchers/matchers.rb +3 -3
  39. data/lib/plugins/inspec-parallel/lib/inspec-parallel/runner.rb +5 -0
  40. data/lib/plugins/inspec-parallel/lib/inspec-parallel/super_reporter/status.rb +1 -0
  41. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +14 -6
  42. metadata +27 -11
  43. data/lib/inspec/utils/telemetry/collector.rb +0 -81
  44. data/lib/inspec/utils/telemetry/data_series.rb +0 -44
  45. data/lib/inspec/utils/telemetry/global_methods.rb +0 -22
@@ -0,0 +1,124 @@
1
+ require "inspec/utils/file_reader"
2
+ require "net/ssh" unless defined?(Net::SSH)
3
+
4
+ # Change module if required
5
+ module Inspec::Resources
6
+ class SshKey < FileResource
7
+ # Every resource requires an internal name.
8
+ name "ssh_key"
9
+
10
+ # Restrict to only run on the below platforms (if none were given,
11
+ # all OS's and cloud API's supported)
12
+ supports platform: "unix"
13
+ supports platform: "windows"
14
+
15
+ desc "public/private SSH key pair test"
16
+
17
+ example <<~EXAMPLE
18
+ describe ssh_key('path: ~/.ssh/id_rsa') do
19
+ its('key_length') { should eq 4096 }
20
+ its('type') { should cmp /rsa/ }
21
+ it { should be_private }
22
+ end
23
+ EXAMPLE
24
+
25
+ include FileReader
26
+
27
+ def initialize(keypath, passphrase = nil)
28
+ skip_resource "The `ssh_key` resource is not yet available on your OS." unless inspec.os.unix? || inspec.os.windows?
29
+ @key_path = set_ssh_key_path(keypath)
30
+ @passphrase = passphrase
31
+ @key = read_ssh_key
32
+ super(@key_path)
33
+ end
34
+
35
+ def public?
36
+ return if @key.nil?
37
+
38
+ @key[:public]
39
+ end
40
+
41
+ def private?
42
+ return if @key.nil?
43
+
44
+ @key[:private]
45
+ end
46
+
47
+ def key_length
48
+ return if @key.nil?
49
+
50
+ @key[:key_length]
51
+ end
52
+
53
+ def type
54
+ return if @key.nil?
55
+
56
+ @key[:type]
57
+ end
58
+
59
+ # Define a resource ID. This is used in reporting engines to uniquely identify the individual resource.
60
+ # This might be a file path, or a process ID, or a cloud instance ID. Only meaningful to the implementation.
61
+ # Must be a string. Defaults to the empty string if not implemented.
62
+ def resource_id
63
+ @key_path || "SSH key"
64
+ end
65
+
66
+ def to_s
67
+ "ssh_key #{@key_path}"
68
+ end
69
+
70
+ private
71
+
72
+ def set_ssh_key_path(keypath)
73
+ if File.exist?(keypath)
74
+ @key_path = keypath
75
+ elsif File.exist?(File.join("#{Dir.home}/.ssh/", keypath))
76
+ @key_path = File.join("#{Dir.home}/.ssh/", keypath)
77
+ else
78
+ raise Inspec::Exceptions::ResourceSkipped, "Can't find file: #{keypath}"
79
+ end
80
+ end
81
+
82
+ def read_ssh_key
83
+ key_data = {}
84
+ key = nil
85
+ filecontent = read_file_content((@key_path), @passphrase)
86
+ raise Inspec::Exceptions::ResourceSkipped, "File is empty: #{@key_path}" if filecontent.split("\n").empty?
87
+
88
+ if filecontent.split("\n")[0].include?("PRIVATE")
89
+ # Net::SSH::KeyFactory does not have support to load private key for DSA
90
+ key = Net::SSH::KeyFactory.load_private_key(@key_path, @passphrase, false)
91
+ unless key.nil?
92
+ key_data[:private] = true
93
+ key_data[:public] = false
94
+ # The data send for ssh type is not in same format so it's good to match on the string
95
+ key_data[:type] = key.ssh_type
96
+ key_data[:key_length] = key_lengh(key)
97
+ end
98
+ else
99
+ key = Net::SSH::KeyFactory.load_public_key(@key_path)
100
+ unless key.nil?
101
+ key_data[:private] = false
102
+ key_data[:public] = true
103
+ # The data send for ssh type is not in same format so it's good to match on the string
104
+ key_data[:type] = key.ssh_type
105
+ key_data[:key_length] = key_lengh(key)
106
+ end
107
+ end
108
+
109
+ key_data
110
+ rescue OpenSSL::PKey::PKeyError => e
111
+ raise Inspec::Exceptions::ResourceFailed, "#{e.message}"
112
+ end
113
+
114
+ def key_lengh(key)
115
+ if key.class.to_s == "OpenSSL::PKey::RSA"
116
+ key.public_key.n.num_bits
117
+ else
118
+ # Unable to get the key lenght data for other types of keys
119
+ # TODO: Need to check if there is any method that will get this info.
120
+ nil
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,2 @@
1
+ # This is just here to make the dynamic loader happy.
2
+ require "inspec/resources/ssh_config"
@@ -44,10 +44,19 @@ module Inspec::Resources
44
44
  # try to get a temp path
45
45
  sql_file_path = upload_sql_file(sql)
46
46
 
47
+ # TODO: Find if there is better way to get the current shell
48
+ current_shell = inspec.command("echo $SHELL")
49
+
50
+ res = current_shell.exit_status
51
+
47
52
  # isql reuires that we have a matching locale set, but does not support C.UTF-8. en_US.UTF-8 is the least evil.
48
- command = "LANG=en_US.UTF-8 SYBASE=#{sybase_home} #{bin} -s\"#{col_sep}\" -w80000 -S #{server} -U #{username} -D #{database} -P \"#{password}\" < #{sql_file_path}"
49
- isql_cmd = inspec.command(command)
53
+ if res == 0 && ( current_shell.stdout&.include?("/csh") || current_shell.stdout&.include?("/tcsh") )
54
+ command = "source #{sybase_home}/SYBASE.csh; setenv LANG en_US.UTF-8; #{bin} -s\"#{col_sep}\" -w80000 -S #{server} -U #{username} -D #{database} -P \"#{password}\" < #{sql_file_path}"
55
+ else
56
+ command = "LANG=en_US.UTF-8 SYBASE=#{sybase_home} #{bin} -s\"#{col_sep}\" -w80000 -S #{server} -U #{username} -D #{database} -P \"#{password}\" < #{sql_file_path}"
57
+ end
50
58
 
59
+ isql_cmd = inspec.command(command)
51
60
  # Check for isql errors
52
61
  res = isql_cmd.exit_status
53
62
  raise Inspec::Exceptions::ResourceFailed.new("isql exited with code #{res} and stderr '#{isql_cmd.stderr}', stdout '#{isql_cmd.stdout}'") unless res == 0
@@ -223,7 +223,7 @@ module Inspec::Resources
223
223
  elsif cgroup_content =~ %r{^\d+:[^:]+:/(kubepods)/.+$}
224
224
  @virtualization_data[:system] = $1
225
225
  @virtualization_data[:role] = "guest"
226
- elsif /container=podman/.match?(file_read("/proc/1/environ"))
226
+ elsif /container=podman/.match?(inspec.file("/proc/1/environ").content)
227
227
  @virtualization_data[:system] = "podman"
228
228
  @virtualization_data[:role] = "guest"
229
229
  elsif lxc_version_exists? && cgroup_content =~ %r{\d:[^:]+:/$}
@@ -110,6 +110,7 @@ require "inspec/resources/selinux"
110
110
  require "inspec/resources/service"
111
111
  require "inspec/resources/shadow"
112
112
  require "inspec/resources/ssh_config"
113
+ require "inspec/resources/ssh_key"
113
114
  require "inspec/resources/ssl"
114
115
  require "inspec/resources/sys_info"
115
116
  require "inspec/resources/toml"
data/lib/inspec/rule.rb CHANGED
@@ -375,19 +375,24 @@ module Inspec
375
375
  # only_if mechanism)
376
376
  # Double underscore: not intended to be called as part of the DSL
377
377
  def __apply_waivers
378
+ @__waiver_data = nil
378
379
  control_id = @__rule_id # TODO: control ID slugging
379
- waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
380
-
381
- waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files) unless waiver_files.nil?
382
380
 
383
- return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
381
+ waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
382
+ if waiver_files.nil? || waiver_files.empty?
383
+ # Support for input registry is provided for backward compatibilty with compliance phase of chef-client
384
+ # Chef-client sends waiver information in inputs hash
385
+ input_registry = Inspec::InputRegistry.instance
386
+ waiver_data_via_input = input_registry.inputs_by_profile.dig(__profile_id, control_id)
387
+ return unless waiver_data_via_input && waiver_data_via_input.has_value? && waiver_data_via_input.value.is_a?(Hash)
388
+
389
+ @__waiver_data = waiver_data_via_input.value
390
+ else
391
+ waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files)
392
+ return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
384
393
 
385
- # An InSpec Input is a datastructure that tracks a profile parameter
386
- # over time. Its value can be set by many sources, and it keeps a
387
- # log of each "set" event so that when it is collapsed to a value,
388
- # it can determine the correct (highest priority) value.
389
- # Store in an instance variable for.. later reading???
390
- @__waiver_data = waiver_data_by_profile[control_id]
394
+ @__waiver_data = waiver_data_by_profile[control_id]
395
+ end
391
396
 
392
397
  __waiver_data["skipped_due_to_waiver"] = false
393
398
  __waiver_data["message"] = ""
data/lib/inspec/runner.rb CHANGED
@@ -12,6 +12,7 @@ require "inspec/dist"
12
12
  require "inspec/reporters"
13
13
  require "inspec/runner_rspec"
14
14
  require "chef-licensing"
15
+ require "inspec/utils/telemetry"
15
16
  # spec requirements
16
17
 
17
18
  module Inspec
@@ -89,7 +90,12 @@ module Inspec
89
90
  end
90
91
 
91
92
  def configure_transport
92
- @backend = Inspec::Backend.create(@conf)
93
+ backend = Inspec::Backend.create(@conf)
94
+ set_backend(backend)
95
+ end
96
+
97
+ def set_backend(new_backend)
98
+ @backend = new_backend
93
99
  @test_collector.backend = @backend
94
100
  end
95
101
 
@@ -145,7 +151,7 @@ module Inspec
145
151
  get_check_example(m, a, b)
146
152
  end.compact
147
153
 
148
- examples.map { |example| total_checks += example.examples.count }
154
+ examples.map { |example| total_checks += example.descendant_filtered_examples.count }
149
155
 
150
156
  unless control_describe_checks.empty?
151
157
  # controls with empty tests are avoided
@@ -174,6 +180,7 @@ module Inspec
174
180
  }
175
181
 
176
182
  Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
183
+ Inspec::Telemetry.run_starting(runner: self, conf: @conf)
177
184
  load
178
185
  run_tests(with)
179
186
  rescue ChefLicensing::SoftwareNotEntitled
@@ -222,6 +229,7 @@ module Inspec
222
229
  @run_data = @test_collector.run(with)
223
230
  # dont output anything if we want a report
224
231
  render_output(@run_data) unless @conf["report"]
232
+ Inspec::Telemetry.run_ending(runner: self, run_data: @run_data, conf: @conf)
225
233
  @test_collector.exit_code
226
234
  end
227
235
 
@@ -3,8 +3,7 @@ require "rubocop-ast"
3
3
  module Inspec
4
4
  class Profile
5
5
  class AstHelper
6
- class CollectorBase
7
- include Parser::AST::Processor::Mixin
6
+ class CollectorBase < Parser::AST::Processor
8
7
  include RuboCop::AST::Traversal
9
8
 
10
9
  attr_reader :memo
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+ require "chef-licensing"
3
+ require "securerandom" unless defined?(SecureRandom)
4
+ require "digest" unless defined?(Digest)
5
+ require_relative "../../dist"
6
+ module Inspec
7
+ class Telemetry
8
+ class Base
9
+ VERSION = 2.0
10
+ TYPE = "job"
11
+ JOB_TYPE = "InSpec"
12
+
13
+ attr_accessor :scratch
14
+
15
+ def fetch_license_ids
16
+ Inspec::Log.debug "Fetching license IDs for telemetry"
17
+ @license_keys ||= ChefLicensing.license_keys
18
+ end
19
+
20
+ def create_wrapper
21
+ Inspec::Log.debug "Initialising wrapper for telemetry"
22
+ {
23
+ version: VERSION,
24
+ createdTimeUTC: Time.now.getutc.iso8601,
25
+ environment: Inspec::Telemetry::RunContextProbe.guess_run_context,
26
+ licenseIds: fetch_license_ids,
27
+ source: "#{Inspec::Dist::EXEC_NAME}:#{Inspec::VERSION}",
28
+ type: TYPE,
29
+ }
30
+ end
31
+
32
+ def note_feature_usage(feature_name)
33
+ @scratch ||= {}
34
+ @scratch[:features] ||= []
35
+ @scratch[:features] << feature_name
36
+ end
37
+
38
+ def run_starting(_opts = {})
39
+ Inspec::Log.debug "Initiating telemetry for InSpec"
40
+ @scratch ||= {}
41
+ @scratch[:features] ||= []
42
+ @scratch[:run_start_time] = Time.now.getutc.iso8601
43
+ end
44
+
45
+ def run_ending(opts)
46
+ note_per_run_features(opts)
47
+
48
+ payload = create_wrapper
49
+
50
+ train_platform = opts[:runner].backend.backend.platform
51
+ payload[:platform] = train_platform.name
52
+
53
+ payload[:jobs] = [{
54
+ type: JOB_TYPE,
55
+
56
+ # Target platform info
57
+ environment: {
58
+ host: obscure(URI(opts[:runner].backend.backend.uri).host) || "unknown",
59
+ os: train_platform.name,
60
+ version: train_platform.release,
61
+ architecture: train_platform.arch || "",
62
+ id: train_platform.uuid,
63
+ },
64
+
65
+ runtime: Inspec::VERSION,
66
+ content: [], # one content == one profile
67
+ steps: [], # one step == one control
68
+ }]
69
+
70
+ opts[:run_data][:profiles].each do |profile|
71
+ payload[:jobs][0][:content] << {
72
+ name: obscure(profile[:name]),
73
+ version: profile[:version],
74
+ sha256: profile[:sha256],
75
+ maintainer: profile[:maintainer] || "",
76
+ type: "profile",
77
+ }
78
+
79
+ profile[:controls].each do |control|
80
+ payload[:jobs][0][:steps] << {
81
+ id: obscure(control[:id]),
82
+ name: "inspec-control",
83
+ description: control[:desc] || "",
84
+ target: {
85
+ mode: opts[:runner].backend.backend.backend_type,
86
+ id: opts[:runner].backend.backend.platform.uuid,
87
+ },
88
+ resources: [],
89
+ features: [],
90
+ tags: format_control_tags(control[:tags]),
91
+ }
92
+
93
+ control[:results]&.each do |resource_block|
94
+ payload[:jobs][0][:steps].last[:resources] << {
95
+ type: "inspec-resource",
96
+ name: resource_block[:resource_class],
97
+ id: obscure(resource_block[:resource_title].respond_to?(:resource_id) ? resource_block[:resource_title].resource_id : nil) || "unknown",
98
+ }
99
+ end
100
+
101
+ # Per-control features.
102
+ payload[:jobs][0][:steps].last[:features] = scratch[:features].dup
103
+ end
104
+ end
105
+
106
+ Inspec::Log.debug "Final data for telemetry upload -> #{payload}"
107
+ Inspec::Log.debug "Finishing telemetry for InSpec"
108
+ # Return payload object for testing
109
+ payload
110
+ end
111
+
112
+ def format_control_tags(tags)
113
+ tags_list = []
114
+ tags.each do |key, value|
115
+ tags_list << { name: key.to_s, value: (value || "").to_s }
116
+ end
117
+ tags_list
118
+ end
119
+
120
+ # Hash text if non-nil
121
+ def obscure(cleartext)
122
+ return nil if cleartext.nil?
123
+ return nil if cleartext.empty?
124
+
125
+ Digest::SHA2.new(256).hexdigest(cleartext)
126
+ end
127
+
128
+ def note_per_run_features(opts)
129
+ note_all_invoked_features
130
+ note_gem_dependency_usage(opts)
131
+ end
132
+
133
+ def note_all_invoked_features
134
+ Inspec::Feature.list_all_invoked_features.each do |feature|
135
+ Inspec::Telemetry.note_feature_usage(feature.to_s)
136
+ end
137
+ end
138
+
139
+ def note_gem_dependency_usage(opts)
140
+ unless opts[:runner].target_profiles.map do |tp|
141
+ tp.metadata.gem_dependencies + \
142
+ tp.locked_dependencies.list.map { |_k, v| v.profile.metadata.gem_dependencies }.flatten
143
+ end.flatten.empty?
144
+ Inspec::Telemetry.note_feature_usage("inspec-gem-deps-in-profiles")
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ require_relative "base"
3
+ require "faraday" unless defined?(Faraday)
4
+ require "inspec/utils/licensing_config"
5
+ module Inspec
6
+ class Telemetry
7
+ class HTTP < Base
8
+ TELEMETRY_JOBS_PATH = "v1/job"
9
+ TELEMETRY_URL = if ChefLicensing::Config.license_server_url&.match?("acceptance")
10
+ ENV["CHEF_TELEMETRY_URL"]
11
+ else
12
+ "https://services.chef.io/telemetry/"
13
+ end
14
+ def run_ending(opts)
15
+ payload = super
16
+ response = connection.post(TELEMETRY_JOBS_PATH) do |req|
17
+ req.body = payload.to_json
18
+ end
19
+ if response.success?
20
+ Inspec::Log.debug "HTTP connection with Telemetry Client successful."
21
+ Inspec::Log.debug "HTTP response from Telemetry Client -> #{response.to_hash}"
22
+ true
23
+ else
24
+ Inspec::Log.debug "HTTP connection with Telemetry Client faced an error."
25
+ Inspec::Log.debug "HTTP error -> #{response.to_hash[:body]["error"]}" if response.to_hash[:body] && response.to_hash[:body]["error"]
26
+ false
27
+ end
28
+ rescue Faraday::ConnectionFailed
29
+ Inspec::Log.debug "HTTP connection failure with telemetry url -> #{TELEMETRY_URL}"
30
+ end
31
+
32
+ def connection
33
+ Faraday.new(url: TELEMETRY_URL) do |config|
34
+ config.request :json
35
+ config.response :json
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+ require_relative "base"
3
+ module Inspec
4
+ class Telemetry
5
+ class Null < Base
6
+ def run_starting(_opts); end
7
+ def run_ending(_opts); end
8
+ end
9
+ end
10
+ end
11
+
@@ -1,9 +1,21 @@
1
1
  module Inspec
2
- module Telemetry
2
+ class Telemetry
3
3
  # Guesses the run context of InSpec - how were we invoked?
4
4
  # All stack values here are determined experimentally
5
5
 
6
6
  class RunContextProbe
7
+ # Guess if we are running under Automate
8
+ def self.under_automate?
9
+ # Currently assume we are under automate if we have an automate-based reporter
10
+ Inspec::Config.cached[:reporter]
11
+ .keys
12
+ .map(&:to_s)
13
+ .any? { |n| n =~ /automate/ }
14
+ end
15
+
16
+ # Guess, using stack introspection, if we were called under
17
+ # test-kitchen, cli, audit-cookbook, or otherwise.
18
+ # TODO add compliance-phase of chef-infra
7
19
  def self.guess_run_context(stack = nil)
8
20
  stack ||= caller_locations
9
21
  return "test-kitchen" if kitchen?(stack)
@@ -1,3 +1,74 @@
1
- require "inspec/utils/telemetry/collector"
2
- require "inspec/utils/telemetry/data_series"
3
- require "inspec/utils/telemetry/global_methods"
1
+ require "time" unless defined?(Time.zone_offset)
2
+ require "chef-licensing"
3
+ require_relative "telemetry/null"
4
+ require_relative "telemetry/http"
5
+ require_relative "telemetry/run_context_probe"
6
+
7
+ module Inspec
8
+ class Telemetry
9
+
10
+ @@instance = nil
11
+ @@config = nil
12
+
13
+ def self.instance
14
+ @@instance ||= determine_backend_class.new
15
+ end
16
+
17
+ def self.determine_backend_class
18
+ # Don't perform telemetry action for other InSpec distros
19
+ # Don't perform telemetry action if running under Automate - Automate does LDC tracking for us
20
+ # Don't perform telemetry action if license is a commercial license
21
+
22
+ if Inspec::Dist::EXEC_NAME != "inspec" ||
23
+ Inspec::Telemetry::RunContextProbe.under_automate? ||
24
+ license&.license_type&.downcase == "commercial"
25
+
26
+ Inspec::Log.debug "Determined telemetry operation is not applicable and hence aborting it."
27
+ return Inspec::Telemetry::Null
28
+ end
29
+
30
+ if Inspec::Dist::EXEC_NAME == "inspec" && telemetry_disabled?
31
+ # Issue a warning if an InSpec user is explicitly trying to opt out of telemetry using cli option
32
+ Inspec::Log.warn "Telemetry opt-out is not permissible."
33
+ end
34
+
35
+ Inspec::Log.debug "Determined HTTP instance for telemetry"
36
+
37
+ Inspec::Telemetry::HTTP
38
+ end
39
+
40
+ def self.license
41
+ Inspec::Log.debug "Fetching license context for telemetry check"
42
+ @license = ChefLicensing.license_context
43
+ end
44
+
45
+ ######
46
+ # These class methods make it convenient to call from anywhere within the InSpec codebase.
47
+ ######
48
+ def self.run_starting(opts)
49
+ @@config ||= opts[:conf]
50
+ instance.run_starting(opts)
51
+ rescue StandardError => e
52
+ Inspec::Log.debug "Encountered error in Telemetry start run call -> #{e.message}"
53
+ end
54
+
55
+ def self.run_ending(opts)
56
+ @@config ||= opts[:conf]
57
+ instance.run_ending(opts)
58
+ rescue StandardError => e
59
+ Inspec::Log.debug "Encountered error in Telemetry end run call -> #{e.message}"
60
+ end
61
+
62
+ def self.note_feature_usage(feature_name)
63
+ instance.note_feature_usage(feature_name)
64
+ end
65
+
66
+ def self.config
67
+ @@config
68
+ end
69
+
70
+ def self.telemetry_disabled?
71
+ config.telemetry_options["enable_telemetry"].nil? ? false : !config.telemetry_options["enable_telemetry"]
72
+ end
73
+ end
74
+ end
@@ -19,7 +19,7 @@ module Waivers
19
19
  row_hash.delete("control_id")
20
20
  row_hash.delete_if { |k, v| k.nil? || v.nil? }
21
21
 
22
- waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
22
+ waiver_data_hash[control_id] = row_hash if control_id && !(row_hash.nil? || row_hash.empty?)
23
23
  end
24
24
 
25
25
  waiver_data_hash
@@ -25,7 +25,7 @@ module Waivers
25
25
  row_hash.delete_if { |k, v| k.nil? || v.nil? }
26
26
  end
27
27
 
28
- waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
28
+ waiver_data_hash[control_id] = row_hash if control_id && !(row_hash.nil? || row_hash.empty?)
29
29
  end
30
30
  waiver_data_hash
31
31
  rescue Exception => e
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "6.6.0".freeze
2
+ VERSION = "6.8.1".freeze
3
3
  end
data/lib/inspec.rb CHANGED
@@ -19,7 +19,6 @@ require "inspec/rspec_extensions"
19
19
  require "inspec/globals"
20
20
  require "inspec/impact"
21
21
  require "inspec/utils/telemetry"
22
- require "inspec/utils/telemetry/global_methods"
23
22
 
24
23
  require "inspec/plugin/v2"
25
24
  require "inspec/plugin/v1"
@@ -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)
302
+ def format_actual(actual, negate = false)
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(false), actual]
304
+ "\n%s\n got: %s\n\n(compared using `cmp` matcher)\n" % [format_expectation(negate), 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
319
+ format_actual actual, true
320
320
  end
321
321
 
322
322
  description do
@@ -22,7 +22,10 @@ module InspecPlugins
22
22
 
23
23
  def run
24
24
  initiate_background_run if run_in_background # running a process as daemon changes parent process pid
25
+ original_stdout_stream = ChefLicensing::Config.output
25
26
  until invocations.empty? && @child_tracker.empty?
27
+ # Changing output to STDERR to avoid the output interruption between runs
28
+ ChefLicensing::Config.output = STDERR
26
29
  while should_start_more_jobs?
27
30
  if Inspec.locally_windows?
28
31
  spawn_another_process
@@ -35,6 +38,8 @@ module InspecPlugins
35
38
  cleanup_child_processes
36
39
  sleep 0.1
37
40
  end
41
+ # Reset output to the original STDOUT stream as a safe measure.
42
+ ChefLicensing::Config.output = original_stdout_stream
38
43
 
39
44
  # Requires renaming operations on windows only
40
45
  # Do Rename and delete operations after all child processes have exited successfully
@@ -44,6 +44,7 @@ module InspecPlugins::Parallelism
44
44
  status_by_pid[pid][:last_control] = title
45
45
  status_by_pid[pid][:last_status] = status
46
46
 
47
+ sleep 0.5
47
48
  paint
48
49
  end
49
50