inspec 1.48.0 → 1.49.2

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -0
  3. data/CHANGELOG.md +40 -16
  4. data/Rakefile +1 -1
  5. data/docs/resources/bond.md.erb +6 -1
  6. data/docs/resources/mysql_session.md.erb +24 -12
  7. data/docs/resources/passwd.md.erb +1 -1
  8. data/docs/resources/xml.md.erb +7 -2
  9. data/docs/shell.md +22 -0
  10. data/inspec.gemspec +1 -1
  11. data/lib/bundles/inspec-artifact/cli.rb +0 -2
  12. data/lib/bundles/inspec-compliance/api.rb +58 -3
  13. data/lib/bundles/inspec-compliance/cli.rb +1 -1
  14. data/lib/bundles/inspec-habitat/profile.rb +1 -1
  15. data/lib/fetchers/url.rb +1 -1
  16. data/lib/inspec/base_cli.rb +3 -1
  17. data/lib/inspec/cli.rb +11 -1
  18. data/lib/inspec/control_eval_context.rb +13 -2
  19. data/lib/inspec/dependencies/lockfile.rb +0 -2
  20. data/lib/inspec/dsl_shared.rb +8 -0
  21. data/lib/inspec/library_eval_context.rb +12 -1
  22. data/lib/inspec/metadata.rb +13 -44
  23. data/lib/inspec/objects/attribute.rb +1 -1
  24. data/lib/inspec/plugins/resource.rb +18 -2
  25. data/lib/inspec/profile.rb +17 -11
  26. data/lib/inspec/profile_context.rb +9 -3
  27. data/lib/inspec/profile_vendor.rb +1 -1
  28. data/lib/inspec/resource.rb +5 -0
  29. data/lib/inspec/rspec_json_formatter.rb +3 -3
  30. data/lib/inspec/rule.rb +1 -1
  31. data/lib/inspec/runner.rb +13 -5
  32. data/lib/inspec/schema.rb +1 -1
  33. data/lib/inspec/shell.rb +1 -1
  34. data/lib/inspec/version.rb +1 -1
  35. data/lib/resources/aide_conf.rb +0 -2
  36. data/lib/resources/apache_conf.rb +9 -2
  37. data/lib/resources/auditd.rb +0 -1
  38. data/lib/resources/auditd_rules.rb +0 -2
  39. data/lib/resources/bond.rb +4 -0
  40. data/lib/resources/crontab.rb +1 -1
  41. data/lib/resources/docker.rb +1 -1
  42. data/lib/resources/elasticsearch.rb +1 -1
  43. data/lib/resources/file.rb +2 -0
  44. data/lib/resources/groups.rb +29 -5
  45. data/lib/resources/grub_conf.rb +1 -1
  46. data/lib/resources/os.rb +8 -20
  47. data/lib/resources/package.rb +20 -21
  48. data/lib/resources/platform.rb +112 -0
  49. data/lib/resources/port.rb +1 -1
  50. data/lib/resources/processes.rb +1 -1
  51. data/lib/resources/registry_key.rb +1 -1
  52. data/lib/resources/service.rb +1 -1
  53. data/lib/resources/virtualization.rb +1 -1
  54. data/lib/resources/x509_certificate.rb +1 -1
  55. data/lib/resources/xml.rb +1 -0
  56. metadata +5 -10
@@ -13,7 +13,7 @@ module Inspec
13
13
  # as the basic DSL of the control files (describe, control, title,
14
14
  # etc).
15
15
  #
16
- class ControlEvalContext # rubocop:disable Metrics/ClassLength
16
+ class ControlEvalContext
17
17
  # Create the context for controls. This includes all components of the DSL,
18
18
  # including matchers and resources.
19
19
  #
@@ -117,10 +117,21 @@ module Inspec
117
117
  end
118
118
 
119
119
  define_method :register_control do |control, &block|
120
- if @skip_file || !profile_context_owner.profile_supports_os?
120
+ if @skip_file
121
121
  ::Inspec::Rule.set_skip_rule(control, true)
122
122
  end
123
123
 
124
+ unless profile_context_owner.profile_supports_platform?
125
+ platform = inspec.platform
126
+ msg = "Profile #{profile_context_owner.profile_id} is not supported on platform #{platform.name}/#{platform.release}."
127
+ ::Inspec::Rule.set_skip_rule(control, msg)
128
+ end
129
+
130
+ unless profile_context_owner.profile_supports_inspec_version?
131
+ msg = "Profile #{profile_context_owner.profile_id} is not supported on InSpec version (#{Inspec::VERSION})."
132
+ ::Inspec::Rule.set_skip_rule(control, msg)
133
+ end
134
+
124
135
  profile_context_owner.register_rule(control, &block) unless control.nil?
125
136
  end
126
137
 
@@ -28,7 +28,6 @@ module Inspec
28
28
  from_content(content)
29
29
  end
30
30
 
31
- # rubocop:disable Style/GuardClause
32
31
  def self.validate_lockfile_version!(version)
33
32
  if version < MINIMUM_SUPPORTED_VERSION
34
33
  raise <<~EOF
@@ -49,7 +48,6 @@ module Inspec
49
48
  EOF
50
49
  end
51
50
  end
52
- # rubocop:enable Style/GuardClause
53
51
 
54
52
  attr_reader :version, :deps
55
53
  def initialize(lockfile_content_hash)
@@ -18,6 +18,14 @@ module Inspec
18
18
  # We cannot rely on libraries residing on disk however.
19
19
  # TODO: Sandboxing.
20
20
  content, path, line = @require_loader.load(rbpath)
21
+
22
+ # If we are in the realm of libraries and the LibraryEvalContext
23
+ # we should have access to the __inspec_binding, which is a Binding
24
+ # context that provides the correct plane to evaluate all required files to.
25
+ # It will ensure that embedded calls to `require` still call this
26
+ # method and get loaded from their correct paths.
27
+ return __inspec_binding.eval(content, path, line) if defined?(__inspec_binding)
28
+
21
29
  eval(content, TOPLEVEL_BINDING, path, line) # rubocop:disable Security/Eval
22
30
  end
23
31
  end
@@ -37,11 +37,22 @@ module Inspec
37
37
  include Inspec::DSL::RequireOverride
38
38
  def initialize(require_loader)
39
39
  @require_loader = require_loader
40
+ @inspec_binding = nil
41
+ end
42
+
43
+ def __inspec_binding
44
+ @inspec_binding
40
45
  end
41
46
  end
42
47
 
43
48
  c3.const_set(:Inspec, c2)
44
- c3.new(require_loader)
49
+ res = c3.new(require_loader)
50
+
51
+ # Provide the local binding for this context which is necessary for
52
+ # calls to `require` to create all dependent objects in the correct
53
+ # context.
54
+ res.instance_variable_set('@inspec_binding', res.instance_eval('binding'))
55
+ res
45
56
  end
46
57
  end
47
58
  end
@@ -14,7 +14,7 @@ module Inspec
14
14
  # A Metadata object may be created and finalized with invalid data.
15
15
  # This allows the check CLI command to analyse the issues.
16
16
  # Use valid? to determine if the metadata is coherent.
17
- class Metadata # rubocop:disable Metrics/ClassLength
17
+ class Metadata
18
18
  attr_reader :ref
19
19
  attr_accessor :params, :content
20
20
  def initialize(ref, logger = nil)
@@ -36,6 +36,7 @@ module Inspec
36
36
  summary
37
37
  description
38
38
  version
39
+ inspec_version
39
40
  }.each do |name|
40
41
  define_method name.to_sym do |arg|
41
42
  params[name.to_sym] = arg
@@ -52,38 +53,16 @@ module Inspec
52
53
  # already.
53
54
  end
54
55
 
55
- def is_supported?(os, entry)
56
- name = entry[:'os-name'] || entry[:os]
57
- family = entry[:'os-family']
58
- release = entry[:release]
59
-
60
- # return true if the backend matches the supported OS's
61
- # fields act as masks, i.e. any value configured for os-name, os-family,
62
- # or release must be met by the backend; any field that is nil acts as
63
- # a glob expression i.e. is true
64
-
65
- # os name is both saved in :family and :name, so check both
66
- name_ok = name.nil? ||
67
- os[:name] == name || os[:family] == name
68
-
69
- family_check = family.to_s + '?'
70
- family_ok = family.nil? || os[:family] == family ||
71
- (
72
- os.respond_to?(family_check) &&
73
- # this call will return true if the family matches
74
- os.method(family_check).call
75
- )
76
-
77
- # ensure we do have a string if we have a non-nil value eg. 16.06
78
- release_ok = release.nil? || os[:release] == release
79
-
80
- # we want to make sure that all matchers are true
81
- name_ok && family_ok && release_ok
82
- end
83
-
84
56
  def inspec_requirement
85
- inspec = params[:supports].find { |x| !x[:inspec].nil? } || {}
86
- Gem::Requirement.create(inspec[:inspec])
57
+ inspec_in_supports = params[:supports].find { |x| !x[:inspec].nil? }
58
+ if inspec_in_supports
59
+ Inspec::Log.warn '[DEPRECATED] The use of inspec.yml `supports:inspec` is deprecated and will be removed in InSpec 2.0. Please use `inspec_version` instead.'
60
+ Gem::Requirement.create(inspec_in_supports[:inspec])
61
+ else
62
+ # using Gem::Requirement here to allow nil values which
63
+ # translate to [">= 0"]
64
+ Gem::Requirement.create(params[:inspec_version])
65
+ end
87
66
  end
88
67
 
89
68
  def supports_runtime?
@@ -91,18 +70,8 @@ module Inspec
91
70
  inspec_requirement.satisfied_by?(running)
92
71
  end
93
72
 
94
- def supports_transport?(backend)
95
- # with no supports specified, always return true, as there are no
96
- # constraints on the supported backend; it is equivalent to putting
97
- # all fields into accept-all mode
98
- return true if params[:supports].empty?
99
-
100
- found = params[:supports].find do |entry|
101
- is_supported?(backend.os, entry)
102
- end
103
-
104
- # finally, if we found a supported entry, we are good to go
105
- !found.nil?
73
+ def supports_platform?(backend)
74
+ backend.platform.supported?(params[:supports])
106
75
  end
107
76
 
108
77
  # return all warn and errors
@@ -27,7 +27,7 @@ module Inspec
27
27
  end
28
28
 
29
29
  def default
30
- @opts[:default] || DEFAULT_ATTRIBUTE.new
30
+ @opts.key?(:default) ? @opts[:default] : DEFAULT_ATTRIBUTE.new
31
31
  end
32
32
 
33
33
  def title
@@ -28,6 +28,12 @@ module Inspec
28
28
  __resource_registry[@name].desc(description)
29
29
  end
30
30
 
31
+ def supports(criteria = nil)
32
+ return if criteria.nil?
33
+ Inspec::Resource.supports[@name] ||= []
34
+ Inspec::Resource.supports[@name].push(criteria)
35
+ end
36
+
31
37
  def example(example = nil)
32
38
  return if example.nil?
33
39
  __resource_registry[@name].example(example)
@@ -37,18 +43,22 @@ module Inspec
37
43
  Inspec::Resource.registry
38
44
  end
39
45
 
40
- def __register(name, obj) # rubocop:disable Metrics/MethodLength
41
- cl = Class.new(obj) do
46
+ def __register(name, obj) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
47
+ cl = Class.new(obj) do # rubocop:disable Metrics/BlockLength
42
48
  attr_reader :resource_exception_message
43
49
 
44
50
  def initialize(backend, name, *args)
45
51
  @resource_skipped = false
46
52
  @resource_failed = false
53
+ @supports = Inspec::Resource.supports[name]
47
54
 
48
55
  # attach the backend to this instance
49
56
  @__backend_runner__ = backend
50
57
  @__resource_name__ = name
51
58
 
59
+ # check resource supports
60
+ check_supports unless @supports.nil?
61
+
52
62
  # call the resource initializer
53
63
  begin
54
64
  super(*args)
@@ -69,6 +79,12 @@ module Inspec
69
79
  @example = example
70
80
  end
71
81
 
82
+ def check_supports
83
+ status = inspec.platform.supported?(@supports)
84
+ skip_msg = "Resource #{@__resource_name__.capitalize} is not supported on platform #{inspec.platform.name}/#{inspec.platform.release}."
85
+ skip_resource(skip_msg) unless status
86
+ end
87
+
72
88
  def skip_resource(message)
73
89
  @resource_skipped = true
74
90
  @resource_exception_message = message
@@ -21,7 +21,7 @@ require 'inspec/dependencies/lockfile'
21
21
  require 'inspec/dependencies/dependency_set'
22
22
 
23
23
  module Inspec
24
- class Profile # rubocop:disable Metrics/ClassLength
24
+ class Profile
25
25
  extend Forwardable
26
26
 
27
27
  def self.resolve_target(target, cache)
@@ -43,7 +43,7 @@ module Inspec
43
43
  next if content[key].nil?
44
44
  # remove prefix
45
45
  rel = Pathname.new(key).relative_path_from(Pathname.new('vendor')).to_s
46
- tar = Pathname.new(opts[:cache].path).join(rel)
46
+ tar = Pathname.new(opts[:vendor_cache].path).join(rel)
47
47
 
48
48
  FileUtils.mkdir_p tar.dirname.to_s
49
49
  Inspec::Log.debug "Copy #{tar} to cache directory"
@@ -56,7 +56,7 @@ module Inspec
56
56
  rp = file_provider.relative_provider
57
57
 
58
58
  # copy embedded dependecies into global cache
59
- copy_deps_into_cache(rp, opts) unless opts[:cache].nil?
59
+ copy_deps_into_cache(rp, opts) unless opts[:vendor_cache].nil?
60
60
 
61
61
  reader = Inspec::SourceReader.resolve(rp)
62
62
  if reader.nil?
@@ -67,14 +67,14 @@ module Inspec
67
67
  end
68
68
 
69
69
  def self.for_fetcher(fetcher, opts)
70
- opts[:cache] = opts[:cache] || Cache.new
70
+ opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
71
71
  path, writable = fetcher.fetch
72
72
  for_path(path, opts.merge(target: fetcher.target, writable: writable))
73
73
  end
74
74
 
75
75
  def self.for_target(target, opts = {})
76
- opts[:cache] = opts[:cache] || Cache.new
77
- fetcher = resolve_target(target, opts[:cache])
76
+ opts[:vendor_cache] = opts[:vendor_cache] || Cache.new
77
+ fetcher = resolve_target(target, opts[:vendor_cache])
78
78
  for_fetcher(fetcher, opts)
79
79
  end
80
80
 
@@ -92,7 +92,7 @@ module Inspec
92
92
  @controls = options[:controls] || []
93
93
  @writable = options[:writable] || false
94
94
  @profile_id = options[:id]
95
- @cache = options[:cache] || Cache.new
95
+ @cache = options[:vendor_cache] || Cache.new
96
96
  @attr_values = options[:attributes]
97
97
  @tests_collected = false
98
98
  @libraries_loaded = false
@@ -136,15 +136,21 @@ module Inspec
136
136
  # @returns [TrueClass, FalseClass]
137
137
  #
138
138
  def supported?
139
- supports_os? && supports_runtime?
139
+ supports_platform? && supports_runtime?
140
140
  end
141
141
 
142
- def supports_os?
143
- metadata.supports_transport?(@backend)
142
+ def supports_platform?
143
+ if @supports_platform.nil?
144
+ @supports_platform = metadata.supports_platform?(@backend)
145
+ end
146
+ @supports_platform
144
147
  end
145
148
 
146
149
  def supports_runtime?
147
- metadata.supports_runtime?
150
+ if @supports_runtime.nil?
151
+ @supports_runtime = metadata.supports_runtime?
152
+ end
153
+ @supports_runtime
148
154
  end
149
155
 
150
156
  def params
@@ -11,7 +11,7 @@ require 'securerandom'
11
11
  require 'inspec/objects/attribute'
12
12
 
13
13
  module Inspec
14
- class ProfileContext # rubocop:disable Metrics/ClassLength
14
+ class ProfileContext
15
15
  def self.for_profile(profile, backend, attributes)
16
16
  new(profile.name, backend, { 'profile' => profile,
17
17
  'attributes' => attributes,
@@ -63,10 +63,16 @@ module Inspec
63
63
  @control_eval_context = nil
64
64
  end
65
65
 
66
- def profile_supports_os?
66
+ def profile_supports_platform?
67
67
  return true if @conf['profile'].nil?
68
68
 
69
- @conf['profile'].supports_os?
69
+ @conf['profile'].supports_platform?
70
+ end
71
+
72
+ def profile_supports_inspec_version?
73
+ return true if @conf['profile'].nil?
74
+
75
+ @conf['profile'].supports_runtime?
70
76
  end
71
77
 
72
78
  def remove_rule(id)
@@ -48,7 +48,7 @@ module Inspec
48
48
 
49
49
  def profile_opts
50
50
  {
51
- cache: Inspec::Cache.new(cache_path.to_s),
51
+ vendor_cache: Inspec::Cache.new(cache_path.to_s),
52
52
  backend: Inspec::Backend.create(target: 'mock://'),
53
53
  }
54
54
  end
@@ -16,6 +16,10 @@ module Inspec
16
16
  @registry ||= default_registry
17
17
  end
18
18
 
19
+ def self.supports
20
+ @supports ||= {}
21
+ end
22
+
19
23
  def self.new_registry
20
24
  default_registry.dup
21
25
  end
@@ -131,6 +135,7 @@ require 'resources/packages'
131
135
  require 'resources/parse_config'
132
136
  require 'resources/passwd'
133
137
  require 'resources/pip'
138
+ require 'resources/platform'
134
139
  require 'resources/port'
135
140
  require 'resources/postgres'
136
141
  require 'resources/postgres_conf'
@@ -110,7 +110,7 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
110
110
  end
111
111
  end
112
112
 
113
- class InspecRspecJson < InspecRspecMiniJson # rubocop:disable Metrics/ClassLength
113
+ class InspecRspecJson < InspecRspecMiniJson
114
114
  RSpec::Core::Formatters.register self, :stop, :dump_summary
115
115
  attr_writer :backend
116
116
 
@@ -279,7 +279,7 @@ class InspecRspecJson < InspecRspecMiniJson # rubocop:disable Metrics/ClassLengt
279
279
  end
280
280
  end
281
281
 
282
- class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
282
+ class InspecRspecCli < InspecRspecJson
283
283
  RSpec::Core::Formatters.register self, :close
284
284
 
285
285
  case RUBY_PLATFORM
@@ -685,7 +685,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
685
685
  # This class wraps a control hash object to provide a useful inteface for
686
686
  # maintaining the associated profile, ids, results, title, etc.
687
687
  #
688
- class Control # rubocop:disable Metrics/ClassLength
688
+ class Control
689
689
  include Comparable
690
690
 
691
691
  STATUS_TYPES = {
@@ -8,7 +8,7 @@ require 'inspec/describe'
8
8
  require 'inspec/expect'
9
9
 
10
10
  module Inspec
11
- class Rule # rubocop:disable Metrics/ClassLength
11
+ class Rule
12
12
  include ::RSpec::Matchers
13
13
 
14
14
  #
@@ -28,7 +28,7 @@ module Inspec
28
28
  # r.run
29
29
  # ```
30
30
  #
31
- class Runner # rubocop:disable Metrics/ClassLength
31
+ class Runner
32
32
  extend Forwardable
33
33
 
34
34
  def_delegator :@test_collector, :report
@@ -40,9 +40,10 @@ module Inspec
40
40
  @conf[:logger] ||= Logger.new(nil)
41
41
  @target_profiles = []
42
42
  @controls = @conf[:controls] || []
43
+ @depends = @conf[:depends] || []
43
44
  @ignore_supports = @conf[:ignore_supports]
44
45
  @create_lockfile = @conf[:create_lockfile]
45
- @cache = Inspec::Cache.new(@conf[:cache])
46
+ @cache = Inspec::Cache.new(@conf[:vendor_cache])
46
47
  @test_collector = @conf.delete(:test_collector) || begin
47
48
  require 'inspec/runner_rspec'
48
49
  RunnerRspec.new(@conf)
@@ -168,7 +169,7 @@ module Inspec
168
169
  #
169
170
  def add_target(target, _opts = [])
170
171
  profile = Inspec::Profile.for_target(target,
171
- cache: @cache,
172
+ vendor_cache: @cache,
172
173
  backend: @backend,
173
174
  controls: @controls,
174
175
  attributes: @conf[:attributes])
@@ -185,8 +186,8 @@ module Inspec
185
186
  "InSpec v#{Inspec::VERSION}.\n"
186
187
  end
187
188
 
188
- if !profile.supports_os?
189
- raise "This OS/platform (#{@backend.os[:name]}) is not supported by this profile."
189
+ if !profile.supports_platform?
190
+ raise "This OS/platform (#{@backend.platform.name}/#{@backend.platform.release}) is not supported by this profile."
190
191
  end
191
192
 
192
193
  true
@@ -214,6 +215,13 @@ module Inspec
214
215
  add_target({ 'inspec.yml' => 'name: inspec-shell' })
215
216
  our_profile = @target_profiles.first
216
217
  ctx = our_profile.runner_context
218
+
219
+ # Load local profile dependencies. This is used in inspec shell
220
+ # to provide access to local profiles that add resources.
221
+ @depends
222
+ .map { |x| Inspec::Profile.for_path(x, { profile_context: ctx }) }
223
+ .each(&:load_libraries)
224
+
217
225
  ctx.load(command)
218
226
  end
219
227