inspec-core 6.8.24 → 7.0.95

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +8 -8
  3. data/etc/deprecations.json +42 -4
  4. data/inspec-core.gemspec +14 -7
  5. data/lib/inspec/archive/tar.rb +1 -0
  6. data/lib/inspec/backend.rb +2 -0
  7. data/lib/inspec/base_cli.rb +15 -2
  8. data/lib/inspec/cached_fetcher.rb +17 -1
  9. data/lib/inspec/cli.rb +2 -0
  10. data/lib/inspec/dependencies/cache.rb +47 -7
  11. data/lib/inspec/dsl.rb +44 -10
  12. data/lib/inspec/exceptions.rb +1 -0
  13. data/lib/inspec/fetcher/gem.rb +117 -0
  14. data/lib/inspec/fetcher/git.rb +21 -1
  15. data/lib/inspec/fetcher/local.rb +1 -1
  16. data/lib/inspec/fetcher.rb +1 -0
  17. data/lib/inspec/file_provider.rb +47 -1
  18. data/lib/inspec/metadata.rb +2 -0
  19. data/lib/inspec/plugin/v2/concerns/gem_spec_helper.rb +30 -0
  20. data/lib/inspec/plugin/v2/gem_source_manager.rb +50 -0
  21. data/lib/inspec/plugin/v2/installer.rb +65 -18
  22. data/lib/inspec/plugin/v2/loader.rb +37 -6
  23. data/lib/inspec/plugin/v2/plugin_types/resource_pack.rb +8 -0
  24. data/lib/inspec/plugin/v2.rb +1 -0
  25. data/lib/inspec/profile.rb +22 -3
  26. data/lib/inspec/profile_context.rb +10 -0
  27. data/lib/inspec/resources/audit_policy.rb +8 -2
  28. data/lib/inspec/resources/groups.rb +52 -0
  29. data/lib/inspec/resources.rb +0 -14
  30. data/lib/inspec/rule.rb +2 -0
  31. data/lib/inspec/runner.rb +7 -1
  32. data/lib/inspec/source_reader.rb +2 -0
  33. data/lib/inspec/ui.rb +1 -0
  34. data/lib/inspec/utils/deprecated_core_resources_list.rb +2 -2
  35. data/lib/inspec/utils/deprecation/config_file.rb +39 -3
  36. data/lib/inspec/utils/deprecation/deprecator.rb +10 -3
  37. data/lib/inspec/utils/simpleconfig.rb +2 -0
  38. data/lib/inspec/utils/telemetry/run_context_probe.rb +5 -2
  39. data/lib/inspec/version.rb +1 -1
  40. data/lib/inspec/waiver_file_reader.rb +35 -18
  41. data/lib/inspec.rb +2 -0
  42. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +1 -1
  43. data/lib/plugins/shared/core_plugin_test_helper.rb +1 -1
  44. data/lib/source_readers/gem.rb +67 -0
  45. metadata +82 -43
  46. data/lib/inspec/resources/docker.rb +0 -274
  47. data/lib/inspec/resources/docker_container.rb +0 -116
  48. data/lib/inspec/resources/docker_image.rb +0 -141
  49. data/lib/inspec/resources/docker_object.rb +0 -52
  50. data/lib/inspec/resources/docker_plugin.rb +0 -68
  51. data/lib/inspec/resources/docker_service.rb +0 -95
  52. data/lib/inspec/resources/elasticsearch.rb +0 -165
  53. data/lib/inspec/resources/ibmdb2_conf.rb +0 -65
  54. data/lib/inspec/resources/ibmdb2_session.rb +0 -78
  55. data/lib/inspec/resources/mongodb.rb +0 -69
  56. data/lib/inspec/resources/mongodb_conf.rb +0 -44
  57. data/lib/inspec/resources/mongodb_session.rb +0 -98
  58. data/lib/inspec/resources/opa.rb +0 -26
  59. data/lib/inspec/resources/opa_api.rb +0 -49
  60. data/lib/inspec/resources/opa_cli.rb +0 -57
  61. data/lib/inspec/resources/podman.rb +0 -353
  62. data/lib/inspec/resources/podman_container.rb +0 -84
  63. data/lib/inspec/resources/podman_image.rb +0 -108
  64. data/lib/inspec/resources/podman_network.rb +0 -81
  65. data/lib/inspec/resources/podman_pod.rb +0 -101
  66. data/lib/inspec/resources/podman_volume.rb +0 -87
  67. data/lib/inspec/resources/rabbitmq_conf.rb +0 -2
  68. data/lib/inspec/resources/rabbitmq_config.rb +0 -56
  69. data/lib/inspec/resources/sybase_conf.rb +0 -41
  70. data/lib/inspec/resources/sybase_session.rb +0 -124
  71. data/lib/inspec/utils/podman.rb +0 -24
@@ -1,4 +1,5 @@
1
1
  require "rubygems/package" unless defined?(Gem::Package)
2
+ require "rubygems/package/tar_header" unless defined?(Gem::Package::TarHeader)
2
3
  require "pathname" unless defined?(Pathname)
3
4
  require "zlib" unless defined?(Zlib)
4
5
  require "zip" unless defined?(Zip)
@@ -7,8 +8,12 @@ require "inspec/iaf_file"
7
8
  module Inspec
8
9
  class FileProvider
9
10
  def self.for_path(path)
10
- if path.is_a?(Hash)
11
+ raise "Profile or dependency path not resolved." if path.nil?
12
+
13
+ if path.is_a?(Hash) && !path.key?(:gem)
11
14
  MockProvider.new(path)
15
+ elsif path.is_a?(Hash) && path.key?(:gem)
16
+ GemProvider.new(path)
12
17
  elsif File.directory?(path)
13
18
  DirProvider.new(path)
14
19
  elsif File.exist?(path) && path.end_with?(".tar.gz", "tgz")
@@ -99,6 +104,47 @@ module Inspec
99
104
  end
100
105
  end # class DirProvider
101
106
 
107
+ class GemProvider < FileProvider
108
+ attr_reader :files
109
+ def initialize(gem_info)
110
+ # Determine a path to the gem installation directory
111
+ gem_name = gem_info[:gem]
112
+ bootstrap_file = gem_info[:path]
113
+ gem_version = gem_info[:version]
114
+ if bootstrap_file
115
+ # We were given an explicit path - go two directories up
116
+ parts = Pathname(gem_info[:path]).each_filename.to_a
117
+ @gem_lib_root = File.join(parts.slice(0, parts.length - 2))
118
+ @files = Dir[File.join(Shellwords.shellescape(@gem_lib_root), "**", "*")]
119
+ else
120
+ # Look up where the gem is installed, respecting version
121
+ loader = Inspec::Plugin::V2::Loader.new
122
+ gem_path = loader.find_gem_directory(gem_name, gem_version)
123
+ if gem_path && File.exist?(gem_path)
124
+ gem = DirProvider.new(gem_path)
125
+ @files = gem.files
126
+ gem
127
+ else
128
+ raise Inspec::Exceptions::GemDependencyNotFound, "Dependency does not exist #{gem_name}"
129
+ end
130
+ end
131
+ end
132
+
133
+ def read(file)
134
+ return nil unless files.include?(file)
135
+ return nil unless File.file?(file)
136
+
137
+ File.read(file)
138
+ end
139
+
140
+ def binread(file)
141
+ return nil unless files.include?(file)
142
+ return nil unless File.file?(file)
143
+
144
+ File.binread(file)
145
+ end
146
+ end # class GemProvider
147
+
102
148
  class ZipProvider < FileProvider
103
149
  attr_reader :files
104
150
 
@@ -1,4 +1,6 @@
1
1
  # Copyright 2015 Dominik Richter
2
+ # Copyright © 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates.
3
+ # All Rights Reserved.
2
4
 
3
5
  require "logger"
4
6
  require "rubygems/version"
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Inspec
4
+ module Plugin
5
+ module V2
6
+ # holds all GemSpec related helper functions
7
+ module GemSpecHelper
8
+
9
+ def loaded_recent_most_version_of?(gemspec)
10
+ # Check if the gem is already loaded via bundler
11
+ # In most cases this is true since all Plugins/Resource Packs inherit from inspec-core
12
+ gem_name = gemspec.name
13
+ loaded_gem = Gem.loaded_specs[gem_name]
14
+ return false unless loaded_gem
15
+
16
+ # follow bundler's original philosophy i.e load gems that are recent most
17
+ # This logic works unless there is a pinned version which we don't expect to have since these are managed by us
18
+ if gemspec.version > loaded_gem.version
19
+ # deactivate the lower version specs that are loaded via bundler
20
+ Gem.loaded_specs.delete(gem_name)
21
+ false # so it can re-activate the requested spec
22
+ else
23
+ # don't activate requested gemspec when the already loaded gem is the most recent version
24
+ true
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ require "singleton" unless defined?(Singleton)
3
+ require "forwardable" unless defined?(Forwardable)
4
+ require "chef-licensing"
5
+
6
+ module Inspec::Plugin::V2
7
+ class GemSourceManager
8
+ include Singleton
9
+ extend Forwardable
10
+
11
+ DEFAULT_CHEF_RUBY_GEMS_SERVER = "rubygems.chef.io"
12
+ DEFAULT_USERNAME = "v1"
13
+
14
+ def initialize
15
+ @sources = Gem.sources
16
+ end
17
+
18
+ def_delegator :@sources, :sources
19
+
20
+ def add_chef_rubygems_server
21
+ register_source(chef_rubygems_server)
22
+ end
23
+
24
+ def add(sources)
25
+ Array(sources).each { |source| register_source(source) }
26
+ end
27
+
28
+ private
29
+
30
+ def chef_rubygems_server
31
+ # If there are no license keys, return nil to avoid adding an invalid source
32
+ "https://#{DEFAULT_USERNAME}:#{licenses_string}@#{DEFAULT_CHEF_RUBY_GEMS_SERVER}" unless licenses_string.empty?
33
+ end
34
+
35
+ def register_source(source)
36
+ return if source.nil? # If the source is nil, we don't want to add it
37
+
38
+ gem_source = Gem::Source.new(source)
39
+ sources << gem_source unless sources.include?(gem_source)
40
+ rescue StandardError => e
41
+ raise StandardError, "Unable to add gem source #{source}: #{e.message}"
42
+ end
43
+
44
+ def licenses_string
45
+ ChefLicensing.license_keys.join(",")
46
+ rescue StandardError
47
+ ""
48
+ end
49
+ end
50
+ end
@@ -12,6 +12,9 @@ require "rubygems/uninstaller"
12
12
  require "rubygems/remote_fetcher"
13
13
 
14
14
  require "inspec/plugin/v2/filter"
15
+ require "inspec/plugin/v2/concerns/gem_spec_helper"
16
+
17
+ require "inspec/plugin/v2/gem_source_manager"
15
18
 
16
19
  module Inspec::Plugin::V2
17
20
  # Handles all actions modifying the user's plugin set:
@@ -23,6 +26,7 @@ module Inspec::Plugin::V2
23
26
  class Installer
24
27
  include Singleton
25
28
  extend Forwardable
29
+ include Inspec::Plugin::V2::GemSpecHelper
26
30
 
27
31
  Gem.configuration["verbose"] = false
28
32
 
@@ -45,6 +49,10 @@ module Inspec::Plugin::V2
45
49
  list_installed_plugin_gems.detect { |spec| spec.name == name && spec.version == Gem::Version.new(version) }
46
50
  end
47
51
 
52
+ def ensure_installed(name)
53
+ plugin_installed?(name) || install(name)
54
+ end
55
+
48
56
  # Installs a plugin. Defaults to assuming the plugin provided is a gem, and will try to install
49
57
  # from whatever gemsources `rubygems` thinks it should use.
50
58
  # If it's a gem, installs it and its dependencies to the `gem_path`. The gem is not activated.
@@ -213,7 +221,10 @@ module Inspec::Plugin::V2
213
221
  if opts.key?(:version) && plugin_version_installed?(plugin_name, opts[:version])
214
222
  raise InstallError, "#{plugin_name} version #{opts[:version]} is already installed."
215
223
  else
216
- raise InstallError, "#{plugin_name} is already installed. Use 'inspec plugin update' to change version."
224
+ # Do not redirect to plugin update when using gem based plugin
225
+ unless gem_based_plugin?(opts)
226
+ raise InstallError, "#{plugin_name} is already installed. Use 'inspec plugin update' to change version."
227
+ end
217
228
  end
218
229
  end
219
230
 
@@ -274,7 +285,16 @@ module Inspec::Plugin::V2
274
285
  #===================================================================#
275
286
 
276
287
  def install_from_path(requested_plugin_name, opts)
277
- # Nothing to do here; we will later update the plugins file with the path.
288
+ return unless gem_based_plugin?(opts)
289
+
290
+ local_gem_path = opts[:path]
291
+ Inspec::Log.debug("Installing #{requested_plugin_name} from path #{local_gem_path}")
292
+ raise InstallError, "Gem File not found: #{local_gem_path}" unless File.exist?(local_gem_path)
293
+
294
+ gem_installer = Gem::Installer.at(local_gem_path, install_dir: gem_path, ignore_dependencies: true, document: [])
295
+ gem_installer.install
296
+
297
+ Inspec::Log.debug("Installed gem from path: #{local_gem_path} to #{gem_path}")
278
298
  end
279
299
 
280
300
  def install_from_gem_file(requested_plugin_name, opts)
@@ -296,18 +316,21 @@ module Inspec::Plugin::V2
296
316
  install_gem_to_plugins_dir(plugin_dependency, [requested_local_gem_set])
297
317
  end
298
318
 
299
- def install_from_remote_gems(requested_plugin_name, opts)
319
+ def install_from_remote_gems(requested_plugin_name, opts, source_manager: GemSourceManager.instance)
300
320
  plugin_dependency = Gem::Dependency.new(requested_plugin_name, opts[:version] || "> 0")
321
+ source_manager.add_chef_rubygems_server # ensure CHEF RUBYGEMS server is added to the source
322
+
323
+ # This adds custom gem sources to the memoized `Gem.Sources` for this specific run
324
+ # Note: This will not make any change to the environment Gem source list and
325
+ # in fact will consider all of the environment Gem sources and custom gem sources to resolve deps
326
+ source_manager.add(opts[:source])
301
327
 
302
- # BestSet is rubygems.org API + indexing, APISet is for custom sources
303
- sources = if opts[:source]
304
- Gem::Resolver::APISet.new(URI.join(opts[:source] + "/api/v1/dependencies"))
305
- else
306
- Gem::Resolver::BestSet.new
307
- end
328
+ # BestSet is rubygems.org API + indexing by default
329
+ # `Gem.sources` is injected as a dependency implicitly while BestSet is initialized
330
+ sources = Gem::Resolver::BestSet.new
308
331
 
309
332
  begin
310
- install_gem_to_plugins_dir(plugin_dependency, [sources], opts[:update_mode])
333
+ install_gem_to_plugins_dir(plugin_dependency, [sources], opts[:update_mode], opts: opts)
311
334
  rescue Gem::RemoteFetcher::FetchError => gem_ex
312
335
  # TODO: Give a hint if the host was not resolvable or a 404 occured
313
336
  ex = Inspec::Plugin::V2::InstallError.new(gem_ex.message)
@@ -318,13 +341,16 @@ module Inspec::Plugin::V2
318
341
 
319
342
  def install_gem_to_plugins_dir(new_plugin_dependency, # rubocop: disable Metrics/AbcSize
320
343
  extra_request_sets = [],
321
- update_mode = false)
344
+ update_mode = false, opts: {})
322
345
 
323
346
  # Get a list of all the gems available to us.
324
347
  gem_to_force_update = update_mode ? new_plugin_dependency.name : nil
325
348
  set_available_for_resolution = build_gem_request_universe(extra_request_sets, gem_to_force_update)
326
349
 
327
350
  # Solve the dependency (that is, find a way to install the new plugin and anything it needs)
351
+ # [WARN]: Gem::RequestSet cannot resolve from multiple directories
352
+ # So all dependent gems will be resolved to the GEM_HOME/Gem.default_dir directory
353
+ # assuming everything will be installed in the same dir
328
354
  request_set = Gem::RequestSet.new(new_plugin_dependency)
329
355
 
330
356
  begin
@@ -338,21 +364,37 @@ module Inspec::Plugin::V2
338
364
 
339
365
  # Activate all current plugins before trying to activate the new one
340
366
  loader.list_managed_gems.each do |spec|
341
- next if spec.name == new_plugin_dependency.name && update_mode
367
+ # Skip in case of update mode
368
+ # Skip in case using a gem based plugin
369
+ next if spec.name == new_plugin_dependency.name && (update_mode || gem_based_plugin?(opts))
342
370
 
343
- spec.activate
371
+ # activate the requested gemspec from the Gem::RequestSet
372
+ spec.activate unless loaded_recent_most_version_of?(spec)
344
373
  end
345
374
 
346
375
  # Make sure we remove any previously loaded gem on update
347
- Gem.loaded_specs.delete(new_plugin_dependency.name) if update_mode
376
+ # Make sure we remove any previously loaded gem when trying to use resource pack gem
377
+ # Gem based plugin when updated need to deactivate older version of gem
378
+ Gem.loaded_specs.delete(new_plugin_dependency.name) if update_mode || gem_based_plugin?(opts)
348
379
 
349
380
  # Test activating the solution. This makes sure we do not try to load two different versions
350
381
  # of the same gem on the stack or a malformed dependency.
351
382
  begin
352
383
  solution.each do |activation_request|
353
- unless activation_request.full_spec.activated?
354
- activation_request.full_spec.activate
355
- end
384
+ requested_gemspec = activation_request.full_spec
385
+ next if requested_gemspec.activated?
386
+
387
+ # The specs at this point are pointed to GEM_HOME/Gem.default_dir directory
388
+ # because of the resolved set's assumption that we will install Gems in the same directory
389
+ # In many cases, RubyGems has already loaded gems from default dir
390
+ # Hence at this point it is really only a sanity check
391
+ # And if the requested_gemspec_file has not been downloaded in this default dir do not activate it
392
+ # Activation will be taken care after the downloads
393
+ requested_gemspec_file = File.join(requested_gemspec.gem_dir, "#{requested_gemspec.name}.gemspec")
394
+ next unless File.exist?(requested_gemspec_file) || File.exist?(requested_gemspec.spec_file)
395
+
396
+ # activate the requested gemspec from the Gem::RequestSet
397
+ requested_gemspec.activate unless loaded_recent_most_version_of?(requested_gemspec)
356
398
  end
357
399
  rescue Gem::LoadError => gem_ex
358
400
  ex = Inspec::Plugin::V2::InstallError.new(gem_ex.message)
@@ -375,7 +417,7 @@ module Inspec::Plugin::V2
375
417
  File.write(path_inside_source, spec.to_ruby)
376
418
  end
377
419
  end
378
-
420
+ loader.activate_managed_gems_for_plugin(new_plugin_dependency.name)
379
421
  # Locate the GemVersion for the new dependency and return it
380
422
  solution.detect { |g| g.name == new_plugin_dependency.name }.version
381
423
  end
@@ -536,5 +578,10 @@ module Inspec::Plugin::V2
536
578
 
537
579
  conf_file
538
580
  end
581
+
582
+ def gem_based_plugin?(opts)
583
+ # Param passed by gem fetcher while installing a new gem plugin dependency.
584
+ !!opts[:gem]
585
+ end
539
586
  end
540
587
  end
@@ -2,6 +2,7 @@ require "inspec/log"
2
2
  require "inspec/version"
3
3
  require "inspec/plugin/v2/config_file"
4
4
  require "inspec/plugin/v2/filter"
5
+ require "inspec/plugin/v2/concerns/gem_spec_helper"
5
6
 
6
7
  module Inspec::Plugin::V2
7
8
  class Loader
@@ -10,6 +11,7 @@ module Inspec::Plugin::V2
10
11
  # For {inspec|train}_plugin_name?
11
12
  include Inspec::Plugin::V2::FilterPredicates
12
13
  extend Inspec::Plugin::V2::FilterPredicates
14
+ include Inspec::Plugin::V2::GemSpecHelper
13
15
 
14
16
  def initialize(options = {})
15
17
  @options = options
@@ -68,7 +70,9 @@ module Inspec::Plugin::V2
68
70
  require plugin_details.entry_point
69
71
  else
70
72
  load_path = plugin_details.entry_point
71
- load_path += ".rb" unless plugin_details.entry_point.end_with?(".rb")
73
+ next if load_path.end_with?(".gem") # local resource pack's path will have gem path
74
+
75
+ load_path += ".rb" unless load_path.end_with?(".rb")
72
76
  load load_path
73
77
  end
74
78
  plugin_details.loaded = true
@@ -157,6 +161,33 @@ module Inspec::Plugin::V2
157
161
  self.class.list_managed_gems
158
162
  end
159
163
 
164
+ def self.find_gem_directory(gem_name, version = nil)
165
+ selected_gemspec = find_gemspec_of(gem_name, version)
166
+ selected_gemspec&.full_gem_path
167
+ end
168
+
169
+ def self.find_gemspec_directory(gem_name, version = nil)
170
+ selected_gemspec = find_gemspec_of(gem_name, version)
171
+ selected_gemspec&.loaded_from
172
+ end
173
+
174
+ def self.find_gemspec_of(gem_name, version = nil)
175
+ version = Gem::Version.new(version) if version && !version.is_a?(Gem::Version)
176
+
177
+ list_managed_gems
178
+ .select { |g| g.name == gem_name }
179
+ .sort_by(&:version)
180
+ .yield_self { |gems| version ? gems.find { |g| g.version == version } : gems.last }
181
+ end
182
+
183
+ def find_gemspec_directory(gem_name, version = nil)
184
+ self.class.find_gemspec_directory(gem_name, version)
185
+ end
186
+
187
+ def find_gem_directory(gem_name, version = nil)
188
+ self.class.find_gem_directory(gem_name, version)
189
+ end
190
+
160
191
  # Lists all plugin gems found in the plugin_gem_path.
161
192
  # This is simply all gems that begin with train- or inspec-
162
193
  # and are not on the exclusion list.
@@ -169,8 +200,6 @@ module Inspec::Plugin::V2
169
200
  self.class.list_managed_gems
170
201
  end
171
202
 
172
- private
173
-
174
203
  # 'Activating' a gem adds it to the load path, so 'require "gemname"' will work.
175
204
  # Given a gem name, this activates the gem and all of its dependencies, respecting
176
205
  # version pinning needs.
@@ -208,13 +237,15 @@ module Inspec::Plugin::V2
208
237
  raise ex
209
238
  end
210
239
  solution.each do |activation_request|
211
- next if activation_request.full_spec.activated?
240
+ requested_gemspec = activation_request.full_spec
241
+ next if requested_gemspec.activated?
212
242
 
213
- activation_request.full_spec.activate
214
- # TODO: If we are under Bundler, inform it that we loaded a gem
243
+ requested_gemspec.activate unless loaded_recent_most_version_of?(requested_gemspec)
215
244
  end
216
245
  end
217
246
 
247
+ private
248
+
218
249
  def annotate_status_after_loading(plugin_name)
219
250
  status = registry[plugin_name]
220
251
  return if status.api_generation == 2 # Gen2 have self-annotating superclasses
@@ -0,0 +1,8 @@
1
+ module Inspec::Plugin::V2::PluginType
2
+ class ResourcePack < Inspec::Plugin::V2::PluginBase
3
+ register_plugin_type(:resource_pack)
4
+
5
+ # TODO: Any DSL definitions would go here
6
+
7
+ end
8
+ end
@@ -13,6 +13,7 @@ module Inspec
13
13
  end
14
14
 
15
15
  class InstallError < Inspec::Plugin::V2::GemActionError; end
16
+ class InstallRequiredError < Inspec::Plugin::V2::InstallError; end
16
17
 
17
18
  class PluginExcludedError < Inspec::Plugin::V2::InstallError
18
19
  attr_accessor :details
@@ -1,4 +1,6 @@
1
1
  # Copyright 2015 Dominik Richter
2
+ # Copyright © 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates.
3
+ # All Rights Reserved.
2
4
 
3
5
  require "forwardable" unless defined?(Forwardable)
4
6
  require "openssl" unless defined?(OpenSSL)
@@ -299,7 +301,14 @@ module Inspec
299
301
  # # Pull together waiver
300
302
  waived_control_ids = []
301
303
  waiver_paths.each do |waiver_path|
302
- waiver_content = YAML.load_file(waiver_path)
304
+ # Ruby 3.1 treats YAML load as a dangerous operation by default, requiring us to declare date and time classes as permitted
305
+ # It's not a valid option in 3.0.x
306
+ if Gem.ruby_version >= Gem::Version.new("3.1.0")
307
+ waiver_content = ::YAML.load_file(waiver_path, permitted_classes: [Date, Time])
308
+ else
309
+ waiver_content = YAML.load_file(waiver_path)
310
+ end
311
+
303
312
  unless waiver_content
304
313
  # Note that we will have already issued a detailed warning
305
314
  Inspec::Log.error "YAML parsing error in #{waiver_path}"
@@ -391,6 +400,16 @@ module Inspec
391
400
  included_tags
392
401
  end
393
402
 
403
+ # InSpec 7 introduced gem-based resource packs, so some "profiles" are now gem-based
404
+ # In this case, they are backed by a gem that contains an inspec.yml and a lib/PATH/plugin.rb
405
+ # which contains activator code which needs to be fired.
406
+ def activate_plugin_if_gem_based
407
+ return unless source_reader.is_a?(SourceReaders::GemReader)
408
+
409
+ reg = Inspec::Plugin::V2::Registry.instance
410
+ reg.find_activator(plugin_name: name.to_sym).activate!
411
+ end
412
+
394
413
  def load_libraries
395
414
  return @runner_context if @libraries_loaded
396
415
 
@@ -618,7 +637,7 @@ module Inspec
618
637
  include_tests: include_tests)
619
638
 
620
639
  # Collect all metadata defined in the control block and inputs defined inside the control block
621
- src.ast.each_node { |n|
640
+ src.ast&.each_node { |n|
622
641
  ctl_id_collector.process(n)
623
642
  input_collector.process(n)
624
643
  }
@@ -680,7 +699,7 @@ module Inspec
680
699
  }
681
700
  source_location_ref = @source_reader.target.abs_path(control_filename)
682
701
  Inspec::Profile::AstHelper::TitleCollector.new(group_data)
683
- .process(src.ast.child_nodes.first) # Picking the title defined for the whole controls file
702
+ .process(src.ast&.child_nodes&.first) # Picking the title defined for the whole controls file
684
703
  group_controls = @info_from_parse[:controls].select { |control| control[:source_location][:ref] == source_location_ref }
685
704
  group_data[:controls] = group_controls.map { |control| control[:id] }
686
705
 
@@ -121,6 +121,7 @@ module Inspec
121
121
  @control_subcontexts << context
122
122
  end
123
123
 
124
+ # Expects arrays of arrays of [[content, path, line]]
124
125
  def load_libraries(libs)
125
126
  lib_prefix = "libraries" + File::SEPARATOR
126
127
  autoloads = []
@@ -130,11 +131,20 @@ module Inspec
130
131
  next unless source.end_with?(".rb")
131
132
 
132
133
  path = source
134
+ # Create a list of files (presumably resources) to autoload by stripping the prefixes
135
+ # InSpec < 6: libraries/*.rb
136
+ # In InSpec 7, libraries can be installed under (for example)
137
+ # lib/inspec-test-resources/resources/demo_resource.rb
138
+
133
139
  if source.start_with?(lib_prefix)
134
140
  path = source.sub(lib_prefix, "")
135
141
  no_subdir = File.dirname(path) == "."
136
142
 
137
143
  autoloads.push(path) if no_subdir
144
+ elsif source.match(%r{^lib/.+/resources/.*\.rb})
145
+ # Gem Resource Pack
146
+ path = source.sub(%r{^lib/}, "")
147
+ autoloads.push(path)
138
148
  end
139
149
 
140
150
  @require_loader.add(path, content, source, line)
@@ -39,11 +39,17 @@ module Inspec::Resources
39
39
  # expected result:
40
40
  # Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
41
41
  # WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
42
- result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
42
+ auditpol_cmd = "Auditpol /get /subcategory:'#{key}' /r"
43
+ result ||= inspec.command(auditpol_cmd)
44
+
45
+ unless result.exit_status == 0
46
+ error = result.stdout + "\n" + result.stderr
47
+ raise Inspec::Exceptions::ResourceFailed, "Error while executing #{auditpol_cmd} command: #{error}"
48
+ end
43
49
 
44
50
  # find line
45
51
  target = nil
46
- result.each_line do |s|
52
+ result.stdout.each_line do |s|
47
53
  target = s.strip if s =~ /\b.*#{key}.*\b/
48
54
  end
49
55
 
@@ -200,8 +200,60 @@ module Inspec::Resources
200
200
  # implements generic unix groups via /etc/group
201
201
  class UnixGroup < GroupInfo
202
202
  def groups
203
+ get_group_info
204
+ end
205
+
206
+ private
207
+
208
+ def get_group_info
209
+ # First, try to fetch group info using getent
210
+ group_info = fetch_group_info_using_getent
211
+
212
+ return group_info unless group_info.empty?
213
+
214
+ # If getent fails, fallback to reading group info from /etc/group using inspec.etc_group.entries
215
+ Inspec::Log.debug("Falling back to reading group info from /etc/group as getent is unavailable or failed.")
203
216
  inspec.etc_group.entries
204
217
  end
218
+
219
+ # Fetches group information using the getent utility
220
+ def fetch_group_info_using_getent
221
+ # Find getent utility on the system
222
+ bin = find_getent_utility
223
+
224
+ # If getent is available, fetch group info
225
+ return [] unless bin
226
+
227
+ cmd = inspec.command("#{bin} group")
228
+ return parse_group_info(cmd) if cmd.exit_status.to_i == 0
229
+
230
+ # If getent fails, log the error and return an empty array
231
+ Inspec::Log.debug("Failed to execute #{bin} group: #{cmd.stderr}.")
232
+ []
233
+ end
234
+
235
+ # Parses group info from the command output
236
+ def parse_group_info(cmd)
237
+ cmd.stdout.strip.split("\n").map do |line|
238
+ name, password, gid, members = line.split(":")
239
+ {
240
+ "name" => name,
241
+ "password" => password,
242
+ "gid" => gid.to_i,
243
+ "members" => members,
244
+ }
245
+ end
246
+ end
247
+
248
+ # Checks if getent exists on the system
249
+ def find_getent_utility
250
+ %w{/usr/bin/getent /bin/getent getent}.each do |cmd|
251
+ return cmd if inspec.command(cmd).exist?
252
+ end
253
+ # Log debug information if getent is not found
254
+ Inspec::Log.debug("Could not find `getent` on your system.")
255
+ nil # Return nil if getent is not found
256
+ end
205
257
  end
206
258
 
207
259
  # OSX uses opendirectory for groups, so `/etc/group` may not be fully accurate
@@ -30,12 +30,6 @@ require "inspec/resources/cron"
30
30
  require "inspec/resources/timezone"
31
31
  require "inspec/resources/dh_params"
32
32
  require "inspec/resources/directory"
33
- require "inspec/resources/docker"
34
- require "inspec/resources/docker_container"
35
- require "inspec/resources/docker_image"
36
- require "inspec/resources/docker_plugin"
37
- require "inspec/resources/docker_service"
38
- require "inspec/resources/elasticsearch"
39
33
  require "inspec/resources/etc_fstab"
40
34
  require "inspec/resources/etc_group"
41
35
  require "inspec/resources/etc_hosts_allow_deny"
@@ -48,8 +42,6 @@ require "inspec/resources/groups"
48
42
  require "inspec/resources/grub_conf"
49
43
  require "inspec/resources/host"
50
44
  require "inspec/resources/http"
51
- require "inspec/resources/ibmdb2_conf"
52
- require "inspec/resources/ibmdb2_session"
53
45
  require "inspec/resources/iis_app"
54
46
  require "inspec/resources/iis_app_pool"
55
47
  require "inspec/resources/iis_site"
@@ -64,9 +56,6 @@ require "inspec/resources/key_rsa"
64
56
  require "inspec/resources/ksh"
65
57
  require "inspec/resources/limits_conf"
66
58
  require "inspec/resources/login_defs"
67
- require "inspec/resources/mongodb"
68
- require "inspec/resources/mongodb_conf"
69
- require "inspec/resources/mongodb_session"
70
59
  require "inspec/resources/mount"
71
60
  require "inspec/resources/mssql_session"
72
61
  require "inspec/resources/mssql_sys_conf"
@@ -82,8 +71,6 @@ require "inspec/resources/oneget"
82
71
  require "inspec/resources/oracle"
83
72
  require "inspec/resources/oracledb_conf"
84
73
  require "inspec/resources/oracledb_listener_conf"
85
- require "inspec/resources/opa_cli"
86
- require "inspec/resources/opa_api"
87
74
  require "inspec/resources/oracledb_session"
88
75
  require "inspec/resources/os"
89
76
  require "inspec/resources/os_env"
@@ -102,7 +89,6 @@ require "inspec/resources/postgres_ident_conf"
102
89
  require "inspec/resources/postgres_session"
103
90
  require "inspec/resources/powershell"
104
91
  require "inspec/resources/processes"
105
- require "inspec/resources/rabbitmq_config"
106
92
  require "inspec/resources/registry_key"
107
93
  require "inspec/resources/security_identifier"
108
94
  require "inspec/resources/security_policy"
data/lib/inspec/rule.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  # copyright: 2015, Dominik Richter
2
+ # Copyright © 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates.
3
+ # All Rights Reserved.
2
4
 
3
5
  require "method_source"
4
6
  require "date"