Class: Inspec::ControlEvalContext

Inherits:
Object
  • Object
show all
Includes:
DSL, DSL::RequireOverride
Defined in:
lib/inspec/control_eval_context.rb

Overview

ControlEvalContext constructs an anonymous class that control files will be instance_exec’d against.

The anonymous class includes the given passed resource_dsl as well as the basic DSL of the control files (describe, control, title, etc).

Instance Attribute Summary collapse

Attributes included from DSL

#backend

Instance Method Summary collapse

Methods included from DSL::RequireOverride

#__ruby_require, #require

Methods included from DSL

filter_included_controls, #include_controls, load_spec_files_for_profile, #method_missing, method_missing_resource, #require_controls, #require_resource

Constructor Details

#initialize(profile_context, resources_dsl, backend, conf, dependencies, require_loader, skip_only_if_eval) ⇒ ControlEvalContext

Returns a new instance of ControlEvalContext.



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/inspec/control_eval_context.rb', line 23

def initialize(profile_context, resources_dsl, backend, conf, dependencies, require_loader, skip_only_if_eval)
  @profile_context = profile_context
  @resources_dsl = resources_dsl
  @backend = backend
  @conf = conf
  @dependencies = dependencies
  @require_loader = require_loader
  @skip_file_message = nil
  @skip_file = false
  @skip_only_if_eval = skip_only_if_eval

  extend resources_dsl # TODO: remove? push to method_missing?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Inspec::DSL

Instance Attribute Details

#confObject

Returns the value of attribute conf.



21
22
23
# File 'lib/inspec/control_eval_context.rb', line 21

def conf
  @conf
end

#profile_contextObject Also known as: profile_context_owner

Returns the value of attribute profile_context.



19
20
21
# File 'lib/inspec/control_eval_context.rb', line 19

def profile_context
  @profile_context
end

#resources_dslObject

Returns the value of attribute resources_dsl.



20
21
22
# File 'lib/inspec/control_eval_context.rb', line 20

def resources_dsl
  @resources_dsl
end

#skip_fileObject

Returns the value of attribute skip_file.



18
19
20
# File 'lib/inspec/control_eval_context.rb', line 18

def skip_file
  @skip_file
end

Instance Method Details

#add_resource(name, new_res) ⇒ Object



103
104
105
106
107
108
109
# File 'lib/inspec/control_eval_context.rb', line 103

def add_resource(name, new_res)
  resources_dsl.module_exec do
    define_method name.to_sym do |*args|
      new_res.new(@backend, name.to_s, *args)
    end
  end
end

#add_resources(context) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/inspec/control_eval_context.rb', line 111

def add_resources(context)
  # # TODO: write real unit tests for this and then make this change:
  # dsl = context.to_resources_dsl
  # self.class.include dsl
  # Inspec::Rule.include dsl

  self.class.class_eval do
    include context.to_resources_dsl
  end

  # TODO: seriously consider getting rid of the NPM model
  extend context.to_resources_dsl
end

#add_subcontext(context) ⇒ Object



125
126
127
# File 'lib/inspec/control_eval_context.rb', line 125

def add_subcontext(context)
  profile_context_owner.add_subcontext(context)
end

#attribute(name, options = {}) ⇒ Object



166
167
168
169
# File 'lib/inspec/control_eval_context.rb', line 166

def attribute(name, options = {})
  Inspec.deprecate(:attrs_dsl, "Input name: #{name}, Profile: #{profile_id}")
  input(name, options)
end

#control(id, opts = {}, &block) ⇒ Object Also known as: rule



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/inspec/control_eval_context.rb', line 55

def control(id, opts = {}, &block)
  opts[:skip_only_if_eval] = @skip_only_if_eval
  if (controls_list_empty? && tags_list_empty?) || control_exist_in_controls_list?(id)
    register_control(Inspec::Rule.new(id, profile_id, resources_dsl, opts, &block))
  elsif !tags_list_empty?
    # Inside elsif rule is initialised before registering it because it enables fetching of control tags
    # This condition is only true when --tags option is used
    inspec_rule = Inspec::Rule.new(id, profile_id, resources_dsl, opts, &block)
    tag_ids = control_tags(inspec_rule)
    register_control(inspec_rule) if tag_exist_in_control_tags?(tag_ids)
  end
end

#control_exist_in_controls_list?(id) ⇒ Boolean

Check if the given control exist in the –controls option

Returns:

  • (Boolean)


218
219
220
221
222
223
224
225
226
227
# File 'lib/inspec/control_eval_context.rb', line 218

def control_exist_in_controls_list?(id)
  id_exist_in_list = false
  if profile_config_exist?
    id_exist_in_list = @conf["profile"].include_controls_list.any? do |inclusion|
      # Try to see if the inclusion is a regex, and if it matches
      inclusion == id || (inclusion.is_a?(Regexp) && inclusion =~ id)
    end
  end
  id_exist_in_list
end

#control_tags(inspec_rule) ⇒ Object



70
71
72
73
74
75
76
77
78
79
# File 'lib/inspec/control_eval_context.rb', line 70

def control_tags(inspec_rule)
  all_tags = []
  inspec_rule.tag.each do |key, value|
    all_tags.push(key)
    all_tags.push(value) unless value.nil?
  end
  all_tags.flatten.compact.uniq.map(&:to_s)
rescue
  []
end

#controls_list_empty?Boolean

Returns true if configuration hash is empty or configuration hash does not have the list of controls that needs to be included

Returns:

  • (Boolean)


230
231
232
# File 'lib/inspec/control_eval_context.rb', line 230

def controls_list_empty?
  !@conf.empty? && @conf.key?("profile") && @conf["profile"].include_controls_list.empty? || @conf.empty?
end

#describe(*args, &block) ⇒ Object

Describe allows users to write rspec-like bare describe blocks without declaring an inclosing control. Here, we generate a control for them automatically and then execute the describe block in the context of that control.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/inspec/control_eval_context.rb', line 86

def describe(*args, &block)
  loc = block_location(block, caller(1..1).first)
  id = "(generated from #{loc} #{SecureRandom.hex})"

  res = nil

  rule = Inspec::Rule.new(id, profile_id, resources_dsl, {}) do
    res = describe(*args, &block)
  end

  if controls_list_empty? || control_exist_in_controls_list?(id)
    register_control(rule, &block)
  end

  res
end

#input(input_name, options = {}) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/inspec/control_eval_context.rb', line 148

def input(input_name, options = {})
  if options.empty?
    # Simply an access, no event here
    Inspec::InputRegistry.find_or_register_input(input_name, profile_id).value
  else
    options[:priority] ||= 20
    options[:provider] = :inline_control_code
    evt = Inspec::Input.infer_event(options)
    Inspec::InputRegistry.find_or_register_input(input_name, profile_id, event: evt).value
  end
end

#input_object(input_name) ⇒ Object

Find the Input object, but don’t collapse to a value. Will return nil on a miss.



162
163
164
# File 'lib/inspec/control_eval_context.rb', line 162

def input_object(input_name)
  Inspec::InputRegistry.find_or_register_input(input_name, profile_id)
end

#only_if(message = nil, &block) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/inspec/control_eval_context.rb', line 176

def only_if(message = nil, &block)
  return unless block
  return if @skip_file == true
  return if @skip_only_if_eval == true

  return if block.yield == true

  # Apply `set_skip_rule` for other rules in the same file
  profile_context_owner.rules.values.each do |r|
    sources_match = r.source_file == block.source_location[0]
    Inspec::Rule.set_skip_rule(r, true, message) if sources_match
  end

  @skip_file_message = message
  @skip_file = true
end

#profile_idObject



39
40
41
# File 'lib/inspec/control_eval_context.rb', line 39

def profile_id
  profile_context.profile_id
end

#profile_nameObject



51
52
53
# File 'lib/inspec/control_eval_context.rb', line 51

def profile_name
  profile_id
end

#register_control(control, &block) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/inspec/control_eval_context.rb', line 129

def register_control(control, &block)
  if @skip_file
    ::Inspec::Rule.set_skip_rule(control, true, @skip_file_message)
  end

  unless profile_context_owner.profile_supports_platform?
    platform = inspec.platform
    msg = "Profile `#{profile_id}` is not supported on platform #{platform.name}/#{platform.release}."
    ::Inspec::Rule.set_skip_rule(control, true, msg)
  end

  unless profile_context_owner.profile_supports_inspec_version?
    msg = "Profile `#{profile_id}` is not supported on InSpec version (#{Inspec::VERSION})."
    ::Inspec::Rule.set_skip_rule(control, true, msg)
  end

  profile_context_owner.register_rule(control, &block) unless control.nil?
end

#skip_control(id) ⇒ Object Also known as: skip_rule



171
172
173
# File 'lib/inspec/control_eval_context.rb', line 171

def skip_control(id)
  profile_context_owner.unregister_rule(id)
end

#tag_exist_in_control_tags?(tag_ids) ⇒ Boolean

Check if the given control exist in the –tags option

Returns:

  • (Boolean)


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/inspec/control_eval_context.rb', line 194

def tag_exist_in_control_tags?(tag_ids)
  tag_option_matches_with_list = false
  if !tag_ids.empty? && !tag_ids.nil? && profile_tag_config_exist?
    tag_option_matches_with_list = !(tag_ids & @conf["profile"].include_tags_list).empty?
    unless tag_option_matches_with_list
      @conf["profile"].include_tags_list.any? do |inclusion|
        # Try to see if the inclusion is a regex, and if it matches
        if inclusion.is_a?(Regexp)
          tag_ids.each do |id|
            tag_option_matches_with_list = (inclusion =~ id)
            break if tag_option_matches_with_list
          end
        end
      end
    end
  end
  tag_option_matches_with_list
end

#tags_list_empty?Boolean

Returns:

  • (Boolean)


213
214
215
# File 'lib/inspec/control_eval_context.rb', line 213

def tags_list_empty?
  !@conf.empty? && @conf.key?("profile") && @conf["profile"].include_tags_list.empty? || @conf.empty?
end

#title(arg) ⇒ Object



47
48
49
# File 'lib/inspec/control_eval_context.rb', line 47

def title(arg)
  profile_context_owner.set_header(:title, arg)
end

#to_sObject



43
44
45
# File 'lib/inspec/control_eval_context.rb', line 43

def to_s
  "Control Evaluation Context (#{profile_name})"
end