inspec 0.32.0 → 0.33.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -2
- data/Gemfile +2 -1
- data/README.md +1 -1
- data/docs/dsl_inspec.rst +21 -0
- data/docs/resources.rst +3 -3
- data/docs/ruby_usage.rst +1 -1
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/http.rb +2 -0
- data/lib/inspec/base_cli.rb +1 -1
- data/lib/inspec/control_eval_context.rb +145 -0
- data/lib/inspec/dependencies/dependency_set.rb +21 -6
- data/lib/inspec/dependencies/requirement.rb +13 -6
- data/lib/inspec/dependencies/resolver.rb +2 -2
- data/lib/inspec/dsl.rb +3 -32
- data/lib/inspec/dsl_shared.rb +25 -0
- data/lib/inspec/library_eval_context.rb +47 -0
- data/lib/inspec/plugins/resource.rb +63 -63
- data/lib/inspec/profile.rb +61 -31
- data/lib/inspec/profile_context.rb +48 -140
- data/lib/inspec/resource.rb +13 -7
- data/lib/inspec/rspec_json_formatter.rb +37 -6
- data/lib/inspec/rule.rb +4 -0
- data/lib/inspec/runner.rb +86 -86
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +1 -1
- data/lib/resources/apache_conf.rb +1 -1
- data/lib/resources/mysql.rb +1 -1
- data/lib/resources/powershell.rb +3 -7
- data/lib/resources/service.rb +3 -3
- data/lib/resources/vbscript.rb +17 -1
- metadata +7 -4
@@ -3,7 +3,9 @@
|
|
3
3
|
# author: Christoph Hartmann
|
4
4
|
require 'inspec/log'
|
5
5
|
require 'inspec/rule'
|
6
|
-
require 'inspec/
|
6
|
+
require 'inspec/resource'
|
7
|
+
require 'inspec/library_eval_context'
|
8
|
+
require 'inspec/control_eval_context'
|
7
9
|
require 'inspec/require_loader'
|
8
10
|
require 'securerandom'
|
9
11
|
require 'inspec/objects/attribute'
|
@@ -14,7 +16,7 @@ module Inspec
|
|
14
16
|
new(profile.name, backend, { 'profile' => profile })
|
15
17
|
end
|
16
18
|
|
17
|
-
attr_reader :attributes, :rules, :profile_id
|
19
|
+
attr_reader :attributes, :rules, :profile_id, :resource_registry
|
18
20
|
def initialize(profile_id, backend, conf)
|
19
21
|
if backend.nil?
|
20
22
|
fail 'ProfileContext is initiated with a backend == nil. ' \
|
@@ -25,17 +27,35 @@ module Inspec
|
|
25
27
|
@conf = conf.dup
|
26
28
|
@rules = {}
|
27
29
|
@subcontexts = []
|
28
|
-
@dependencies = {}
|
29
|
-
@dependencies = conf['profile'].locked_dependencies unless conf['profile'].nil?
|
30
30
|
@require_loader = ::Inspec::RequireLoader.new
|
31
31
|
@attributes = []
|
32
|
-
|
32
|
+
# A local resource registry that only contains resources defined
|
33
|
+
# in the transitive dependency tree of the loaded profile.
|
34
|
+
@resource_registry = Inspec::Resource.new_registry
|
35
|
+
@library_eval_context = Inspec::LibraryEvalContext.create(@resource_registry, @require_loader)
|
36
|
+
end
|
37
|
+
|
38
|
+
def dependencies
|
39
|
+
if @conf['profile'].nil?
|
40
|
+
{}
|
41
|
+
else
|
42
|
+
@conf['profile'].locked_dependencies
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_resources_dsl
|
47
|
+
Inspec::Resource.create_dsl(@backend, @resource_registry)
|
48
|
+
end
|
49
|
+
|
50
|
+
def control_eval_context
|
51
|
+
@control_eval_context ||= begin
|
52
|
+
ctx = Inspec::ControlEvalContext.create(self, to_resources_dsl)
|
53
|
+
ctx.new(@backend, @conf, dependencies, @require_loader)
|
54
|
+
end
|
33
55
|
end
|
34
56
|
|
35
57
|
def reload_dsl
|
36
|
-
|
37
|
-
ctx = create_context(resources_dsl, rule_context(resources_dsl))
|
38
|
-
@profile_context = ctx.new(@backend, @conf, @dependencies, @require_loader)
|
58
|
+
@control_eval_context = nil
|
39
59
|
end
|
40
60
|
|
41
61
|
def all_rules
|
@@ -44,6 +64,12 @@ module Inspec
|
|
44
64
|
ret
|
45
65
|
end
|
46
66
|
|
67
|
+
def add_resources(context)
|
68
|
+
@resource_registry.merge!(context.resource_registry)
|
69
|
+
control_eval_context.add_resources(context)
|
70
|
+
reload_dsl
|
71
|
+
end
|
72
|
+
|
47
73
|
def add_subcontext(context)
|
48
74
|
@subcontexts << context
|
49
75
|
end
|
@@ -65,21 +91,29 @@ module Inspec
|
|
65
91
|
# load all files directly that are flat inside the libraries folder
|
66
92
|
autoloads.each do |path|
|
67
93
|
next unless path.end_with?('.rb')
|
68
|
-
|
94
|
+
load_library_file(*@require_loader.load(path)) unless @require_loader.loaded?(path)
|
69
95
|
end
|
70
|
-
|
71
96
|
reload_dsl
|
72
97
|
end
|
73
98
|
|
74
|
-
def
|
99
|
+
def load_control_file(*args)
|
100
|
+
load_with_context(control_eval_context, *args)
|
101
|
+
end
|
102
|
+
alias load load_control_file
|
103
|
+
|
104
|
+
def load_library_file(*args)
|
105
|
+
load_with_context(@library_eval_context, *args)
|
106
|
+
end
|
107
|
+
|
108
|
+
def load_with_context(context, content, source = nil, line = nil)
|
75
109
|
Inspec::Log.debug("Loading #{source || '<anonymous content>'} into #{self}")
|
76
110
|
@current_load = { file: source }
|
77
111
|
if content.is_a? Proc
|
78
|
-
|
112
|
+
context.instance_eval(&content)
|
79
113
|
elsif source.nil? && line.nil?
|
80
|
-
|
114
|
+
context.instance_eval(content)
|
81
115
|
else
|
82
|
-
|
116
|
+
context.instance_eval(content, source || 'unknown', line || 1)
|
83
117
|
end
|
84
118
|
end
|
85
119
|
|
@@ -121,131 +155,5 @@ module Inspec
|
|
121
155
|
return rid.to_s if pid.to_s.empty?
|
122
156
|
pid.to_s + '/' + rid.to_s
|
123
157
|
end
|
124
|
-
|
125
|
-
# Create the context for controls. This includes all components of the DSL,
|
126
|
-
# including matchers and resources.
|
127
|
-
#
|
128
|
-
# @param [ResourcesDSL] resources_dsl which has all resources to attach
|
129
|
-
# @return [RuleContext] the inner context of rules
|
130
|
-
def rule_context(resources_dsl)
|
131
|
-
require 'rspec/core/dsl'
|
132
|
-
Class.new(Inspec::Rule) do
|
133
|
-
include RSpec::Core::DSL
|
134
|
-
include resources_dsl
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# Creates the heart of the profile context:
|
139
|
-
# An instantiated object which has all resources registered to it
|
140
|
-
# and exposes them to the a test file. The profile context serves as a
|
141
|
-
# container for all profiles which are registered. Within the context
|
142
|
-
# profiles get access to all DSL calls for creating tests and controls.
|
143
|
-
#
|
144
|
-
# @param outer_dsl [OuterDSLClass]
|
145
|
-
# @return [ProfileContextClass]
|
146
|
-
def create_context(resources_dsl, rule_class) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
147
|
-
profile_context_owner = self
|
148
|
-
profile_id = @profile_id
|
149
|
-
|
150
|
-
# rubocop:disable Lint/NestedMethodDefinition
|
151
|
-
Class.new do
|
152
|
-
include Inspec::DSL
|
153
|
-
include resources_dsl
|
154
|
-
|
155
|
-
def initialize(backend, conf, dependencies, require_loader) # rubocop:disable Lint/NestedMethodDefinition, Lint/DuplicateMethods
|
156
|
-
@backend = backend
|
157
|
-
@conf = conf
|
158
|
-
@dependencies = dependencies
|
159
|
-
@require_loader = require_loader
|
160
|
-
@skip_profile = false
|
161
|
-
end
|
162
|
-
|
163
|
-
# Save the toplevel require method to load all ruby dependencies.
|
164
|
-
# It is used whenever the `require 'lib'` is not in libraries.
|
165
|
-
alias_method :__ruby_require, :require
|
166
|
-
|
167
|
-
def require(path)
|
168
|
-
rbpath = path + '.rb'
|
169
|
-
return __ruby_require(path) if !@require_loader.exists?(rbpath)
|
170
|
-
return false if @require_loader.loaded?(rbpath)
|
171
|
-
|
172
|
-
# This is equivalent to calling `require 'lib'` with lib on disk.
|
173
|
-
# We cannot rely on libraries residing on disk however.
|
174
|
-
# TODO: Sandboxing.
|
175
|
-
content, path, line = @require_loader.load(rbpath)
|
176
|
-
eval(content, TOPLEVEL_BINDING, path, line) # rubocop:disable Lint/Eval
|
177
|
-
end
|
178
|
-
|
179
|
-
define_method :title do |arg|
|
180
|
-
profile_context_owner.set_header(:title, arg)
|
181
|
-
end
|
182
|
-
|
183
|
-
def to_s
|
184
|
-
"Profile Context Run #{profile_name}"
|
185
|
-
end
|
186
|
-
|
187
|
-
define_method :profile_name do
|
188
|
-
profile_id
|
189
|
-
end
|
190
|
-
|
191
|
-
define_method :control do |*args, &block|
|
192
|
-
id = args[0]
|
193
|
-
opts = args[1] || {}
|
194
|
-
register_control(rule_class.new(id, profile_id, opts, &block))
|
195
|
-
end
|
196
|
-
|
197
|
-
define_method :describe do |*args, &block|
|
198
|
-
loc = block_location(block, caller[0])
|
199
|
-
id = "(generated from #{loc} #{SecureRandom.hex})"
|
200
|
-
|
201
|
-
res = nil
|
202
|
-
rule = rule_class.new(id, profile_id, {}) do
|
203
|
-
res = describe(*args, &block)
|
204
|
-
end
|
205
|
-
register_control(rule, &block)
|
206
|
-
|
207
|
-
res
|
208
|
-
end
|
209
|
-
|
210
|
-
define_method :add_subcontext do |context|
|
211
|
-
profile_context_owner.add_subcontext(context)
|
212
|
-
end
|
213
|
-
|
214
|
-
define_method :register_control do |control, &block|
|
215
|
-
::Inspec::Rule.set_skip_rule(control, true) if @skip_profile
|
216
|
-
|
217
|
-
profile_context_owner.register_rule(control, &block) unless control.nil?
|
218
|
-
end
|
219
|
-
|
220
|
-
# method for attributes; import attribute handling
|
221
|
-
define_method :attribute do |name, options|
|
222
|
-
profile_context_owner.register_attribute(name, options)
|
223
|
-
end
|
224
|
-
|
225
|
-
define_method :skip_control do |id|
|
226
|
-
profile_context_owner.unregister_rule(id)
|
227
|
-
end
|
228
|
-
|
229
|
-
def only_if
|
230
|
-
return unless block_given?
|
231
|
-
@skip_profile ||= !yield
|
232
|
-
end
|
233
|
-
|
234
|
-
alias_method :rule, :control
|
235
|
-
alias_method :skip_rule, :skip_control
|
236
|
-
|
237
|
-
private
|
238
|
-
|
239
|
-
def block_location(block, alternate_caller)
|
240
|
-
if block.nil?
|
241
|
-
alternate_caller[/^(.+:\d+):in .+$/, 1] || 'unknown'
|
242
|
-
else
|
243
|
-
path, line = block.source_location
|
244
|
-
"#{File.basename(path)}:#{line}"
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
# rubocop:enable all
|
249
|
-
end
|
250
158
|
end
|
251
159
|
end
|
data/lib/inspec/resource.rb
CHANGED
@@ -3,17 +3,20 @@
|
|
3
3
|
# license: All rights reserved
|
4
4
|
# author: Dominik Richter
|
5
5
|
# author: Christoph Hartmann
|
6
|
-
|
7
6
|
require 'inspec/plugins'
|
8
7
|
|
9
8
|
module Inspec
|
10
9
|
class Resource
|
11
|
-
|
12
|
-
|
10
|
+
def self.default_registry
|
11
|
+
@default_registry ||= {}
|
13
12
|
end
|
14
13
|
|
15
14
|
def self.registry
|
16
|
-
@registry ||=
|
15
|
+
@registry ||= default_registry
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.new_registry
|
19
|
+
default_registry.dup
|
17
20
|
end
|
18
21
|
|
19
22
|
# Creates the inner DSL which includes all resources for
|
@@ -22,9 +25,8 @@ module Inspec
|
|
22
25
|
#
|
23
26
|
# @param backend [BackendRunner] exposing the target to resources
|
24
27
|
# @return [ResourcesDSL]
|
25
|
-
def self.create_dsl(backend)
|
28
|
+
def self.create_dsl(backend, my_registry = registry)
|
26
29
|
# need the local name, to use it in the module creation further down
|
27
|
-
my_registry = registry
|
28
30
|
Module.new do
|
29
31
|
my_registry.each do |id, r|
|
30
32
|
define_method id.to_sym do |*args|
|
@@ -41,10 +43,14 @@ module Inspec
|
|
41
43
|
# @param [int] version the resource version to use
|
42
44
|
# @return [Resource] base class for creating a new resource
|
43
45
|
def self.resource(version)
|
46
|
+
validate_resource_dsl_version!(version)
|
47
|
+
Inspec::Plugins::Resource
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.validate_resource_dsl_version!(version)
|
44
51
|
if version != 1
|
45
52
|
fail 'Only resource version 1 is supported!'
|
46
53
|
end
|
47
|
-
Inspec::Plugins::Resource
|
48
54
|
end
|
49
55
|
end
|
50
56
|
|
@@ -164,10 +164,32 @@ class InspecRspecJson < InspecRspecMiniJson
|
|
164
164
|
[info[:name], info]
|
165
165
|
end
|
166
166
|
|
167
|
+
#
|
168
|
+
# TODO(ssd+vj): We should probably solve this by either ensuring the example has
|
169
|
+
# the profile_id of the top level profile when it is included as a dependency, or
|
170
|
+
# by registrying all dependent profiles with the formatter. The we could remove
|
171
|
+
# this heuristic matching.
|
172
|
+
#
|
173
|
+
def example2profile(example, profiles)
|
174
|
+
profiles.values.find { |p| profile_contains_example?(p, example) }
|
175
|
+
end
|
176
|
+
|
177
|
+
def profile_contains_example?(profile, example)
|
178
|
+
# Heuristic for finding the profile an example came from:
|
179
|
+
# Case 1: The profile_id on the example matches the name of the profile
|
180
|
+
# Case 2: The profile contains a control that matches the id of the example
|
181
|
+
if profile[:name] == example[:profile_id]
|
182
|
+
true
|
183
|
+
elsif profile[:controls] && profile[:controls].key?(example[:id])
|
184
|
+
true
|
185
|
+
else
|
186
|
+
false
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
167
190
|
def example2control(example, profiles)
|
168
|
-
|
169
|
-
|
170
|
-
profile[:controls][example[:id]]
|
191
|
+
p = example2profile(example, profiles)
|
192
|
+
p[:controls][example[:id]] if p && p[:controls]
|
171
193
|
end
|
172
194
|
|
173
195
|
def format_example(example)
|
@@ -240,7 +262,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
240
262
|
print_line(
|
241
263
|
color: '', indicator: @indicators['empty'], id: '', profile: '',
|
242
264
|
summary: 'No tests executed.'
|
243
|
-
)
|
265
|
+
) if @current_control.nil?
|
244
266
|
output.puts('')
|
245
267
|
end
|
246
268
|
|
@@ -348,7 +370,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
348
370
|
test_status = x[:status_type]
|
349
371
|
test_color = @colors[test_status]
|
350
372
|
indicator = @indicators[x[:status]]
|
351
|
-
indicator = @indicators['empty'] if
|
373
|
+
indicator = @indicators['empty'] if indicator.nil?
|
352
374
|
msg = x[:message] || x[:skip_message] || x[:code_desc]
|
353
375
|
print_line(
|
354
376
|
color: test_color,
|
@@ -364,9 +386,18 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
364
386
|
control_result = control[:results]
|
365
387
|
title = control_result[0][:code_desc].split[0..1].join(' ')
|
366
388
|
puts ' ' + title
|
389
|
+
# iterate over all describe blocks in anonoymous control block
|
367
390
|
control_result.each do |test|
|
368
391
|
control_id = ''
|
369
|
-
|
392
|
+
# display exceptions
|
393
|
+
unless test[:exception].nil?
|
394
|
+
test_result = test[:message]
|
395
|
+
else
|
396
|
+
# determine title
|
397
|
+
test_result = test[:code_desc].split[2..-1].join(' ')
|
398
|
+
# show error message
|
399
|
+
test_result += "\n" + test[:message] unless test[:message].nil?
|
400
|
+
end
|
370
401
|
status_indicator = test[:status_type]
|
371
402
|
print_line(
|
372
403
|
color: @colors[status_indicator] || '',
|
data/lib/inspec/rule.rb
CHANGED
data/lib/inspec/runner.rb
CHANGED
@@ -14,13 +14,34 @@ require 'inspec/secrets'
|
|
14
14
|
# spec requirements
|
15
15
|
|
16
16
|
module Inspec
|
17
|
+
#
|
18
|
+
# Inspec::Runner coordinates the running of tests and is the main
|
19
|
+
# entry point to the application.
|
20
|
+
#
|
21
|
+
# Users are expected to insantiate a runner, add targets to be run,
|
22
|
+
# and then call the run method:
|
23
|
+
#
|
24
|
+
# ```
|
25
|
+
# r = Inspec::Runner.new()
|
26
|
+
# r.add_target("/path/to/some/profile")
|
27
|
+
# r.add_target("http://url/to/some/profile")
|
28
|
+
# r.run
|
29
|
+
# ```
|
30
|
+
#
|
17
31
|
class Runner # rubocop:disable Metrics/ClassLength
|
18
32
|
extend Forwardable
|
33
|
+
|
34
|
+
def_delegator :@test_collector, :report
|
35
|
+
def_delegator :@test_collector, :reset
|
36
|
+
|
19
37
|
attr_reader :backend, :rules, :attributes
|
20
38
|
def initialize(conf = {})
|
21
39
|
@rules = []
|
22
40
|
@conf = conf.dup
|
23
41
|
@conf[:logger] ||= Logger.new(nil)
|
42
|
+
@target_profiles = []
|
43
|
+
@controls = @conf[:controls] || []
|
44
|
+
@ignore_supports = @conf[:ignore_supports]
|
24
45
|
|
25
46
|
@test_collector = @conf.delete(:test_collector) || begin
|
26
47
|
require 'inspec/runner_rspec'
|
@@ -38,19 +59,31 @@ module Inspec
|
|
38
59
|
@test_collector.tests
|
39
60
|
end
|
40
61
|
|
41
|
-
def normalize_map(hm)
|
42
|
-
res = {}
|
43
|
-
hm.each {|k, v|
|
44
|
-
res[k.to_s] = v
|
45
|
-
}
|
46
|
-
res
|
47
|
-
end
|
48
|
-
|
49
62
|
def configure_transport
|
50
63
|
@backend = Inspec::Backend.create(@conf)
|
51
64
|
@test_collector.backend = @backend
|
52
65
|
end
|
53
66
|
|
67
|
+
def run(with = nil)
|
68
|
+
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
|
69
|
+
Inspec::Log.debug "Backend is #{@backend}"
|
70
|
+
all_controls = []
|
71
|
+
|
72
|
+
@target_profiles.each do |profile|
|
73
|
+
@test_collector.add_profile(profile)
|
74
|
+
profile.locked_dependencies
|
75
|
+
profile.load_libraries
|
76
|
+
@attributes |= profile.runner_context.attributes
|
77
|
+
all_controls += profile.collect_tests
|
78
|
+
end
|
79
|
+
|
80
|
+
all_controls.each do |rule|
|
81
|
+
register_rule(rule)
|
82
|
+
end
|
83
|
+
|
84
|
+
@test_collector.run(with)
|
85
|
+
end
|
86
|
+
|
54
87
|
# determine all attributes before the execution, fetch data from secrets backend
|
55
88
|
def load_attributes(options)
|
56
89
|
attributes = {}
|
@@ -66,15 +99,54 @@ module Inspec
|
|
66
99
|
options['attributes'] = attributes
|
67
100
|
end
|
68
101
|
|
69
|
-
#
|
70
|
-
|
71
|
-
|
102
|
+
#
|
103
|
+
# add_target allows the user to add a target whose tests will be
|
104
|
+
# run when the user calls the run method.
|
105
|
+
#
|
106
|
+
# A target is a path or URL that points to a profile. Using this
|
107
|
+
# target we generate a Profile and a ProfileContext. The content
|
108
|
+
# (libraries, tests, and attributes) from the Profile are loaded
|
109
|
+
# into the ProfileContext.
|
110
|
+
#
|
111
|
+
# If the profile depends on other profiles, those profiles will be
|
112
|
+
# loaded on-demand when include_content or required_content are
|
113
|
+
# called using similar code in Inspec::DSL.
|
114
|
+
#
|
115
|
+
# Once the we've loaded all of the tests files in the profile, we
|
116
|
+
# query the profile for the full list of rules. Those rules are
|
117
|
+
# registered with the @test_collector which is ultimately
|
118
|
+
# responsible for actually running the tests.
|
119
|
+
#
|
120
|
+
# TODO: Deduplicate/clarify the loading code that exists in here,
|
121
|
+
# the ProfileContext, the Profile, and Inspec::DSL
|
122
|
+
#
|
123
|
+
# @params target [String] A path or URL to a profile or raw test.
|
124
|
+
# @params _opts [Hash] Unused, but still here to avoid breaking kitchen-inspec
|
125
|
+
#
|
126
|
+
# @eturns [Inspec::ProfileContext]
|
127
|
+
#
|
128
|
+
def add_target(target, _opts = [])
|
129
|
+
profile = Inspec::Profile.for_target(target, backend: @backend, controls: @controls)
|
72
130
|
fail "Could not resolve #{target} to valid input." if profile.nil?
|
73
|
-
|
131
|
+
@target_profiles << profile if supports_profile?(profile)
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# This is used by inspec-shell and inspec-detect. This should
|
136
|
+
# probably be cleaned up a bit.
|
137
|
+
#
|
138
|
+
# @params [Hash] Options
|
139
|
+
# @returns [Inspec::ProfileContext]
|
140
|
+
#
|
141
|
+
def create_context(options = {})
|
142
|
+
meta = options[:metadata]
|
143
|
+
profile_id = nil
|
144
|
+
profile_id = meta.params[:name] unless meta.nil?
|
145
|
+
Inspec::ProfileContext.new(profile_id, @backend, @conf.merge(options))
|
74
146
|
end
|
75
147
|
|
76
148
|
def supports_profile?(profile)
|
77
|
-
return true if profile.metadata.nil?
|
149
|
+
return true if profile.metadata.nil? || @ignore_supports
|
78
150
|
|
79
151
|
if !profile.metadata.supports_runtime?
|
80
152
|
fail 'This profile requires InSpec version '\
|
@@ -90,61 +162,6 @@ module Inspec
|
|
90
162
|
true
|
91
163
|
end
|
92
164
|
|
93
|
-
# Returns the profile context used to initialize this profile.
|
94
|
-
def add_profile(profile, options = {})
|
95
|
-
return if !options[:ignore_supports] && !supports_profile?(profile)
|
96
|
-
|
97
|
-
@test_collector.add_profile(profile)
|
98
|
-
options[:metadata] = profile.metadata
|
99
|
-
options[:profile] = profile
|
100
|
-
|
101
|
-
libs = profile.libraries.map do |k, v|
|
102
|
-
{ ref: k, content: v }
|
103
|
-
end
|
104
|
-
|
105
|
-
tests = profile.tests.map do |ref, content|
|
106
|
-
r = profile.source_reader.target.abs_path(ref)
|
107
|
-
{ ref: r, content: content }
|
108
|
-
end
|
109
|
-
|
110
|
-
add_content(tests, libs, options)
|
111
|
-
end
|
112
|
-
|
113
|
-
def create_context(options = {})
|
114
|
-
meta = options[:metadata]
|
115
|
-
profile_id = nil
|
116
|
-
profile_id = meta.params[:name] unless meta.nil?
|
117
|
-
Inspec::ProfileContext.new(profile_id, @backend, @conf.merge(options))
|
118
|
-
end
|
119
|
-
|
120
|
-
# Returns the profile context used to evaluate the given content.
|
121
|
-
# Calling this method again will use a different context each time.
|
122
|
-
def add_content(tests, libs, options = {})
|
123
|
-
return if tests.nil? || tests.empty?
|
124
|
-
|
125
|
-
# load all libraries
|
126
|
-
ctx = create_context(options)
|
127
|
-
ctx.load_libraries(libs.map { |x| [x[:content], x[:ref], x[:line]] })
|
128
|
-
|
129
|
-
# hand the context to the profile for further evaluation
|
130
|
-
unless (profile = options[:profile]).nil?
|
131
|
-
profile.runner_context = ctx
|
132
|
-
end
|
133
|
-
|
134
|
-
# evaluate the test content
|
135
|
-
Array(tests).each { |t| add_test_to_context(t, ctx) }
|
136
|
-
|
137
|
-
# merge and collect all attributes
|
138
|
-
@attributes |= ctx.attributes
|
139
|
-
|
140
|
-
# process the resulting rules
|
141
|
-
filter_controls(ctx.all_rules, options[:controls]).each do |rule|
|
142
|
-
register_rule(rule)
|
143
|
-
end
|
144
|
-
|
145
|
-
ctx
|
146
|
-
end
|
147
|
-
|
148
165
|
# In some places we read the rules off of the runner, in other
|
149
166
|
# places we read it off of the profile context. To keep the API's
|
150
167
|
# the same, we provide an #all_rules method here as well.
|
@@ -162,26 +179,8 @@ module Inspec
|
|
162
179
|
new_tests
|
163
180
|
end
|
164
181
|
|
165
|
-
def_delegator :@test_collector, :run
|
166
|
-
def_delegator :@test_collector, :report
|
167
|
-
def_delegator :@test_collector, :reset
|
168
|
-
|
169
182
|
private
|
170
183
|
|
171
|
-
def add_test_to_context(test, ctx)
|
172
|
-
content = test[:content]
|
173
|
-
return if content.nil? || content.empty?
|
174
|
-
ctx.load(content, test[:ref], test[:line])
|
175
|
-
end
|
176
|
-
|
177
|
-
def filter_controls(controls_array, include_list)
|
178
|
-
return controls_array if include_list.nil? || include_list.empty?
|
179
|
-
controls_array.select do |c|
|
180
|
-
id = ::Inspec::Rule.rule_id(c)
|
181
|
-
include_list.include?(id)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
184
|
def block_source_info(block)
|
186
185
|
return {} if block.nil? || !block.respond_to?(:source_location)
|
187
186
|
opts = {}
|
@@ -226,6 +225,7 @@ module Inspec
|
|
226
225
|
end
|
227
226
|
|
228
227
|
def register_rule(rule)
|
228
|
+
Inspec::Log.debug "Registering rule #{rule}"
|
229
229
|
@rules << rule
|
230
230
|
checks = ::Inspec::Rule.prepare_checks(rule)
|
231
231
|
examples = checks.map do |m, a, b|
|