inspec-core 4.18.51 → 4.18.85

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +61 -0
  3. data/README.md +3 -3
  4. data/inspec-core.gemspec +51 -0
  5. data/lib/bundles/inspec-supermarket/cli.rb +1 -0
  6. data/lib/inspec/backend.rb +49 -47
  7. data/lib/inspec/base_cli.rb +2 -2
  8. data/lib/inspec/cached_fetcher.rb +4 -0
  9. data/lib/inspec/cli.rb +5 -0
  10. data/lib/inspec/config.rb +1 -1
  11. data/lib/inspec/control_eval_context.rb +131 -199
  12. data/lib/inspec/dependencies/requirement.rb +1 -1
  13. data/lib/inspec/dependencies/resolver.rb +46 -0
  14. data/lib/inspec/dsl_shared.rb +25 -3
  15. data/lib/inspec/fetcher.rb +0 -3
  16. data/lib/inspec/fetcher/git.rb +4 -0
  17. data/lib/inspec/fetcher/url.rb +1 -2
  18. data/lib/inspec/file_provider.rb +4 -2
  19. data/lib/inspec/library_eval_context.rb +37 -37
  20. data/lib/inspec/plugin/v1/plugin_types/fetcher.rb +27 -0
  21. data/lib/inspec/plugin/v1/plugins.rb +0 -1
  22. data/lib/inspec/profile.rb +8 -6
  23. data/lib/inspec/profile_context.rb +74 -9
  24. data/lib/inspec/profile_vendor.rb +48 -3
  25. data/lib/inspec/resource.rb +192 -41
  26. data/lib/inspec/resources/aide_conf.rb +1 -1
  27. data/lib/inspec/resources/apache_conf.rb +15 -31
  28. data/lib/inspec/resources/command.rb +1 -1
  29. data/lib/inspec/resources/crontab.rb +56 -56
  30. data/lib/inspec/resources/etc_fstab.rb +1 -1
  31. data/lib/inspec/resources/etc_group.rb +1 -1
  32. data/lib/inspec/resources/etc_hosts.rb +2 -3
  33. data/lib/inspec/resources/etc_hosts_allow_deny.rb +1 -1
  34. data/lib/inspec/resources/file.rb +2 -2
  35. data/lib/inspec/resources/filesystem.rb +4 -4
  36. data/lib/inspec/resources/groups.rb +16 -2
  37. data/lib/inspec/resources/iis_app.rb +1 -1
  38. data/lib/inspec/resources/ini.rb +1 -2
  39. data/lib/inspec/resources/mount.rb +2 -2
  40. data/lib/inspec/resources/oracledb_session.rb +1 -1
  41. data/lib/inspec/resources/package.rb +22 -0
  42. data/lib/inspec/resources/passwd.rb +1 -1
  43. data/lib/inspec/resources/platform.rb +36 -36
  44. data/lib/inspec/resources/port.rb +1 -1
  45. data/lib/inspec/resources/postfix_conf.rb +1 -1
  46. data/lib/inspec/resources/service.rb +23 -15
  47. data/lib/inspec/resources/users.rb +3 -3
  48. data/lib/inspec/resources/virtualization.rb +15 -11
  49. data/lib/inspec/resources/x509_certificate.rb +18 -4
  50. data/lib/inspec/resources/xinetd_conf.rb +1 -1
  51. data/lib/inspec/resources/xml.rb +1 -2
  52. data/lib/inspec/rspec_extensions.rb +12 -0
  53. data/lib/inspec/rule.rb +63 -22
  54. data/lib/inspec/utils/filter.rb +2 -0
  55. data/lib/inspec/utils/parser.rb +244 -240
  56. data/lib/inspec/utils/simpleconfig.rb +1 -1
  57. data/lib/inspec/version.rb +1 -1
  58. data/lib/matchers/matchers.rb +11 -10
  59. data/lib/plugins/inspec-compliance/lib/inspec-compliance.rb +3 -0
  60. data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +2 -2
  61. data/lib/plugins/inspec-init/templates/profiles/aws/README.md +192 -0
  62. data/lib/plugins/inspec-init/templates/profiles/aws/attributes.yml +2 -0
  63. data/lib/plugins/inspec-init/templates/profiles/aws/controls/example.rb +39 -0
  64. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +22 -0
  65. data/lib/plugins/inspec-init/templates/profiles/azure/README.md +56 -0
  66. data/lib/plugins/inspec-init/templates/profiles/azure/controls/example.rb +14 -0
  67. data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +14 -0
  68. data/lib/plugins/inspec-init/templates/profiles/gcp/README.md +66 -0
  69. data/lib/plugins/inspec-init/templates/profiles/gcp/attributes.yml +2 -0
  70. data/lib/plugins/inspec-init/templates/profiles/gcp/controls/example.rb +27 -0
  71. data/lib/plugins/inspec-init/templates/profiles/gcp/inspec.yml +19 -0
  72. data/lib/source_readers/inspec.rb +1 -1
  73. metadata +87 -74
  74. data/lib/inspec/plugin/v1/plugin_types/resource.rb +0 -176
  75. data/lib/plugins/inspec-init/templates/profiles/os/libraries/.gitkeep +0 -0
@@ -46,7 +46,7 @@ module Inspec
46
46
  req
47
47
  end
48
48
 
49
- attr_reader :cwd, :opts, :version_constraints
49
+ attr_reader :cwd, :opts, :version_constraints, :cache
50
50
  def initialize(name, version_constraints, config, opts)
51
51
  @name = name
52
52
  @version_constraints = Array(version_constraints)
@@ -57,6 +57,16 @@ module Inspec
57
57
 
58
58
  detect_duplicates(deps, top_level, path_string)
59
59
  deps.each do |dep|
60
+ # Calling dep.resolved_source forces a fetch. Handle any airgap chicanery early.
61
+ if Inspec::Config.cached[:airgap]
62
+ begin
63
+ dep.resolved_source
64
+ rescue Inspec::FetcherFailure
65
+ Inspec::Log.debug("Failed to fetch #{dep.name}, falling back to archives if possible")
66
+ retry if fallback_to_archive_on_fetch_failure(dep)
67
+ end
68
+ end
69
+
60
70
  new_seen_items = seen_items.dup
61
71
  new_path_string = if path_string.empty?
62
72
  dep.name
@@ -82,5 +92,41 @@ module Inspec
82
92
  Inspec::Log.debug("Dependency traversal complete.") if top_level
83
93
  graph
84
94
  end
95
+
96
+ def fallback_to_archive_on_fetch_failure(dep)
97
+ # This facility is intended to handle situations in which
98
+ # the failing dependency *is* available in an archive that we have
99
+ # available as a local dependency. We just need to find the archive and
100
+ # alter the fetcher to refer to information in the archive.
101
+ # Note that the vendor cache already should have the archive inflated
102
+ # for this to work (see warm_cache_from_archives() from profile_vendor.rb)
103
+ # Refs 4727
104
+
105
+ # This is where any existing archives should have been inflated -
106
+ # that is, this is the vendor cache. Each archive would have a lockfile.
107
+ cache_path = dep.cache.path
108
+ worth_retrying = false
109
+
110
+ Dir["#{cache_path}/*/inspec.lock"].each do |lockfile_path|
111
+ lockfile = Inspec::Lockfile.from_file(lockfile_path)
112
+ dep_set = Inspec::DependencySet.from_lockfile(lockfile, dep.opts)
113
+ dep2 = dep_set.dep_list[dep.name]
114
+ next unless dep2
115
+
116
+ if dep.opts.key?(:compliance)
117
+ # This is ugly. The compliance fetcher works differently than the others,
118
+ # and fails at the resolve stage, not the fetch stage. That means we can't
119
+ # tweak the fetcher, we have to tweak the deps opts themselves.
120
+ dep.opts[:sha256] = dep2.opts[:sha256]
121
+ worth_retrying = true
122
+ else
123
+ # All other fetchers can be generalized, because they will survive their constructor.
124
+ fetcher = dep.fetcher.fetcher # Not the CachedFetcher, but its fetcher
125
+ made_a_change = fetcher.update_from_opts(dep2.opts)
126
+ end
127
+ worth_retrying ||= made_a_change
128
+ end
129
+ worth_retrying
130
+ end
85
131
  end
86
132
  end
@@ -8,6 +8,26 @@ module Inspec
8
8
  # It is used whenever the `require 'lib'` is not in libraries.
9
9
  alias __ruby_require require
10
10
 
11
+ ##
12
+ # This is our own require override, to be used in
13
+ # LibraryEvalContext and ControlEvalContext.
14
+ #
15
+ # Any top level libraries file (autoloaded) that requires a
16
+ # second-level libraries file.
17
+ #
18
+ # in load_libraries
19
+ # in top level libraries file to be autoloaded
20
+ # that has a require to a known file that is NOT loaded yet
21
+ #
22
+ # ProfileContext#initialize
23
+ # -> library_eval_context
24
+ #
25
+ # ProfileContext#load_libraries autoload
26
+ # -> load_library_file(@library_eval_context)
27
+ #
28
+ # probably most of this comment is useless, but it was hard to
29
+ # discover so I'm adding it for others.
30
+
11
31
  def require(path)
12
32
  rbpath = path + ".rb"
13
33
  return __ruby_require(path) unless @require_loader.exists?(rbpath)
@@ -23,9 +43,11 @@ module Inspec
23
43
  # context that provides the correct plane to evaluate all required files to.
24
44
  # It will ensure that embedded calls to `require` still call this
25
45
  # method and get loaded from their correct paths.
26
- return __inspec_binding.eval(content, path, line) if defined?(__inspec_binding)
27
-
28
- eval(content, TOPLEVEL_BINDING, path, line) # rubocop:disable Security/Eval
46
+ if defined?(__inspec_binding)
47
+ __inspec_binding.eval(content, path, line)
48
+ else
49
+ eval(content, TOPLEVEL_BINDING, path, line) # rubocop:disable Security/Eval
50
+ end
29
51
  end
30
52
  end
31
53
  end
@@ -43,6 +43,3 @@ end
43
43
  require "inspec/fetcher/local"
44
44
  require "inspec/fetcher/url"
45
45
  require "inspec/fetcher/git"
46
-
47
- # TODO: Remove in 4.0 when Compliance fetcher plugin is created
48
- require "plugins/inspec-compliance/lib/inspec-compliance/api"
@@ -112,6 +112,10 @@ module Inspec::Fetcher
112
112
  source
113
113
  end
114
114
 
115
+ def update_from_opts(opts)
116
+ %i{branch tag ref}.map { |opt_name| update_ivar_from_opt(opt_name, opts) }.any?
117
+ end
118
+
115
119
  private
116
120
 
117
121
  def resolved_ref
@@ -127,8 +127,7 @@ module Inspec::Fetcher
127
127
  end
128
128
 
129
129
  def sha256
130
- file = @archive_path || temp_archive_path
131
- OpenSSL::Digest::SHA256.digest(File.read(file)).unpack("H*")[0]
130
+ @archive_shasum ||= OpenSSL::Digest::SHA256.digest(File.read(@archive_path || temp_archive_path)).unpack("H*")[0]
132
131
  end
133
132
 
134
133
  def file_type_from_remote(remote)
@@ -66,7 +66,7 @@ module Inspec
66
66
  end
67
67
 
68
68
  class DirProvider < FileProvider
69
- attr_reader :files
69
+ attr_reader :files, :path
70
70
  def initialize(path)
71
71
  @files = if File.file?(path)
72
72
  [path]
@@ -209,8 +209,10 @@ module Inspec
209
209
  def walk_tar(path, &callback)
210
210
  tar_file = Zlib::GzipReader.open(path)
211
211
  Gem::Package::TarReader.new(tar_file, &callback)
212
+ rescue => e
213
+ raise Inspec::Error, "Error opening/processing #{path}: #{e.message}"
212
214
  ensure
213
- tar_file.close
215
+ tar_file.close if tar_file
214
216
  end
215
217
  end # class TarProvider
216
218
 
@@ -1,4 +1,4 @@
1
- require "inspec/plugin/v1/plugin_types/resource"
1
+ require "inspec/resource"
2
2
  require "inspec/dsl_shared"
3
3
 
4
4
  module Inspec
@@ -12,44 +12,44 @@ module Inspec
12
12
  # registry used by all dsl methods bound to the resource registry
13
13
  # passed into the #create constructor.
14
14
  #
15
- #
16
15
  class LibraryEvalContext
16
+ # rubocop:disable Naming/ConstantName
17
+ Inspec = :nope! # see #initialize below
18
+ # rubocop:enable Naming/ConstantName
19
+
20
+ ##
21
+ # Include a custom `require` method that gets used when this
22
+ # context is used to eval source. See lib/inspec/dsl_shared.rb for
23
+ # more details.
24
+ include ::Inspec::DSL::RequireOverride
25
+
17
26
  def self.create(registry, require_loader)
18
- c = Class.new do
19
- extend Inspec::ResourceDSL
20
- include Inspec::ResourceBehaviors
21
- define_singleton_method :__resource_registry do
22
- registry
23
- end
24
- end
25
-
26
- c2 = Class.new do
27
- define_singleton_method :resource do |version|
28
- Inspec.validate_resource_dsl_version!(version)
29
- c
30
- end
31
- end
32
-
33
- c3 = Class.new do
34
- include Inspec::DSL::RequireOverride
35
- def initialize(require_loader)
36
- @require_loader = require_loader
37
- @inspec_binding = nil
38
- end
39
-
40
- def __inspec_binding
41
- @inspec_binding
42
- end
43
- end
44
-
45
- c3.const_set(:Inspec, c2)
46
- res = c3.new(require_loader)
47
-
48
- # Provide the local binding for this context which is necessary for
49
- # calls to `require` to create all dependent objects in the correct
50
- # context.
51
- res.instance_variable_set("@inspec_binding", res.instance_eval("binding"))
52
- res
27
+ Class.new(LibraryEvalContext).new(registry, require_loader)
28
+ end
29
+
30
+ # Provide the local binding for this context which is
31
+ # necessary for calls to `require` to create all dependent
32
+ # objects in the correct context.
33
+ attr_accessor :__inspec_binding
34
+
35
+ def initialize(registry, require_loader)
36
+ @require_loader = require_loader
37
+ # rubocop:disable Style/RedundantSelf
38
+ self.__inspec_binding = self.instance_eval { binding }
39
+ # rubocop:enable Style/RedundantSelf
40
+
41
+ @res_klass = Class.new ::Inspec::Resource
42
+ @res_klass.__resource_registry = registry
43
+
44
+ # NOTE: this *must* be a subclass of LibraryEvalContext to work
45
+ self.class.const_set :Inspec, self # BYPASS! See resource below
46
+ end
47
+
48
+ # Fake for Inspec.resource in lib/inspec/resource.rb that provides
49
+ # our own Resource subclass that has its own private __resource_registry
50
+ def resource(version)
51
+ ::Inspec.validate_resource_dsl_version!(version)
52
+ @res_klass
53
53
  end
54
54
  end
55
55
  end
@@ -65,6 +65,33 @@ module Inspec
65
65
  raise "Fetcher #{self} does not implement `cache_key()`. This is required for terminal fetchers."
66
66
  end
67
67
 
68
+ #
69
+ # This optional method may be used after a failed fetch. If the fetcher
70
+ # can be updated with information that might lead to a successful
71
+ # retrieval of alternative content, this method may be called.
72
+ #
73
+ # Default implementation makes a peculiar assumption that the class has
74
+ # a ivar named @archive_shasum and you have a fetcher opt that pairs with
75
+ # it named sha256, and those are the only two that matter for updating.
76
+ #
77
+ # Return TrueClass if the fetcher was updated and a retry is in order
78
+ # Return FalseClass if the update contained no useful information
79
+ # and a retry should not be attempted
80
+ def update_from_opts(opts)
81
+ changed = @archive_shasum != opts[:sha256]
82
+ @archive_shasum = opts[:sha256]
83
+ changed
84
+ end
85
+
86
+ # Helper for above; usful when the subclass ivars whose
87
+ # names exactly match the names of the fetcher options.
88
+ def update_ivar_from_opt(opt_name, opts)
89
+ ivar_sym = "@#{opt_name}".to_sym
90
+ changed = instance_variable_get(ivar_sym) != opts[opt_name]
91
+ instance_variable_set(ivar_sym, opts[opt_name])
92
+ changed
93
+ end
94
+
68
95
  #
69
96
  # relative_target is provided to keep compatibility with 3rd
70
97
  # party plugins.
@@ -5,7 +5,6 @@ module Inspec
5
5
  # NOTE: the autoloading here is rendered moot by the fact that
6
6
  # all core plugins are `require`'d by the base inspec.rb
7
7
  module Plugins
8
- autoload :Resource, "inspec/plugin/v1/plugin_types/resource"
9
8
  autoload :CLI, "inspec/plugin/v1/plugin_types/cli"
10
9
  autoload :Fetcher, "inspec/plugin/v1/plugin_types/fetcher"
11
10
  autoload :SourceReader, "inspec/plugin/v1/plugin_types/source_reader"
@@ -250,6 +250,7 @@ module Inspec
250
250
  # this metadata if the parent profile is supported.
251
251
  if supports_platform? && !d.supports_platform?
252
252
  # since ruby 1.9 hashes are ordered so we can just use index values here
253
+ # TODO: NO! this is a violation of encapsulation to an extreme
253
254
  metadata.dependencies[i][:status] = "skipped"
254
255
  msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'."
255
256
  metadata.dependencies[i][:skip_message] = msg
@@ -259,13 +260,14 @@ module Inspec
259
260
  # load them again when we dive down. This needs to be re-done.
260
261
  metadata.dependencies[i][:status] = "loaded"
261
262
  end
262
- c = d.load_libraries
263
+
264
+ # rubocop:disable Layout/ExtraSpacing
265
+ c = d.load_libraries # !!!RECURSE!!!
263
266
  @runner_context.add_resources(c)
264
267
  end
265
268
 
266
- libs = libraries.map do |path, content|
267
- [content, path]
268
- end
269
+ # TODO: why?!? we own both sides of this code
270
+ libs = libraries.map(&:reverse)
269
271
 
270
272
  @runner_context.load_libraries(libs)
271
273
  @libraries_loaded = true
@@ -312,11 +314,11 @@ module Inspec
312
314
  end
313
315
 
314
316
  # add information about the required inputs
315
- if res[:inputs].nil? || res[:inputs].empty?
317
+ if params[:inputs].nil? || params[:inputs].empty?
316
318
  # convert to array for backwards compatability
317
319
  res[:inputs] = []
318
320
  else
319
- res[:inputs] = res[:inputs].values.map(&:to_hash)
321
+ res[:inputs] = params[:inputs].values.map(&:to_hash)
320
322
  end
321
323
  res[:sha256] = sha256
322
324
  res[:parent_profile] = parent_profile unless parent_profile.nil?
@@ -13,6 +13,7 @@ module Inspec
13
13
  new(profile.name, backend, { "profile" => profile, "check_mode" => profile.check_mode })
14
14
  end
15
15
 
16
+ attr_reader :library_eval_context
16
17
  attr_reader :backend, :profile_name, :profile_id, :resource_registry
17
18
  attr_accessor :rules
18
19
  def initialize(profile_id, backend, conf)
@@ -52,14 +53,18 @@ module Inspec
52
53
  end
53
54
 
54
55
  def to_resources_dsl
55
- Inspec::Resource.create_dsl(self)
56
+ DomainSpecificLunacy.create_dsl(self)
56
57
  end
57
58
 
58
59
  def control_eval_context
59
- @control_eval_context ||= begin
60
- ctx = Inspec::ControlEvalContext.create(self, to_resources_dsl)
61
- ctx.new(@backend, @conf, dependencies, @require_loader, @skip_only_if_eval)
62
- end
60
+ @control_eval_context ||=
61
+ Inspec::ControlEvalContext.new(self,
62
+ to_resources_dsl,
63
+ @backend,
64
+ @conf,
65
+ dependencies,
66
+ @require_loader,
67
+ @skip_only_if_eval)
63
68
  end
64
69
 
65
70
  def reload_dsl
@@ -121,10 +126,14 @@ module Inspec
121
126
 
122
127
  libs.sort_by! { |l| l[1] } # Sort on source path so load order is deterministic
123
128
  libs.each do |content, source, line|
129
+ next unless source.end_with?(".rb")
130
+
124
131
  path = source
125
132
  if source.start_with?(lib_prefix)
126
133
  path = source.sub(lib_prefix, "")
127
- autoloads.push(path) if File.dirname(path) == "."
134
+ no_subdir = File.dirname(path) == "."
135
+
136
+ autoloads.push(path) if no_subdir
128
137
  end
129
138
 
130
139
  @require_loader.add(path, content, source, line)
@@ -132,10 +141,10 @@ module Inspec
132
141
 
133
142
  # load all files directly that are flat inside the libraries folder
134
143
  autoloads.each do |path|
135
- next unless path.end_with?(".rb")
136
-
137
- load_library_file(*@require_loader.load(path)) unless @require_loader.loaded?(path)
144
+ load_library_file(*@require_loader.load(path)) unless
145
+ @require_loader.loaded?(path)
138
146
  end
147
+
139
148
  reload_dsl
140
149
  end
141
150
 
@@ -199,5 +208,61 @@ module Inspec
199
208
 
200
209
  pid.to_s + "/" + rid.to_s
201
210
  end
211
+
212
+ module DomainSpecificLunacy
213
+ def self.create_dsl(profile_context)
214
+ Module.new do
215
+ include DomainSpecificLunacy
216
+ add_methods(profile_context)
217
+ end
218
+ end
219
+
220
+ def self.included(mod)
221
+ mod.extend ClassMethods
222
+ end
223
+
224
+ def resource_class(profile_name, resource_name)
225
+ inner_context = if profile_name == profile_context.profile_id
226
+ profile_context
227
+ else
228
+ profile_context.subcontext_by_name(profile_name)
229
+ end
230
+
231
+ raise ProfileNotFound, "Cannot find profile named: #{profile_name}" if inner_context.nil?
232
+
233
+ inner_context.resource_registry[resource_name]
234
+ end
235
+
236
+ module ClassMethods
237
+ def add_methods(profile_context)
238
+ backend = profile_context.backend
239
+
240
+ define_method(:profile_context) { profile_context }
241
+ define_method(:inspec) { backend }
242
+
243
+ add_registry_methods(profile_context)
244
+ end
245
+
246
+ def add_registry_methods(profile_context)
247
+ be = profile_context.backend
248
+ bec = be.class
249
+
250
+ registry = profile_context.resource_registry
251
+ registry.each do |id, r|
252
+ define_method(id) { |*args| r.new(be, id.to_s, *args) }
253
+
254
+ next if be.respond_to?(id)
255
+
256
+ bec.define_method(id) { |*args| r.new(be, id.to_s, *args) }
257
+ end
258
+ end # add_resource_methods
259
+ end # ClassMethods
260
+ end # DomainSpecificLunacy
261
+ end # ProfileContext
262
+ end
263
+
264
+ if RUBY_VERSION < "2.5"
265
+ class Module
266
+ public :define_method
202
267
  end
203
268
  end