inspec-core 6.6.0 → 6.8.1

Sign up to get free protection for your applications and to get access to all the features.
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