inspec-core 4.18.51 → 4.18.85

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