Class: Inspec::Rule

Inherits:
Object
  • Object
show all
Includes:
RSpec::Matchers
Defined in:
lib/inspec/rule.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, profile_id, opts, &block) ⇒ Rule

Returns a new instance of Rule.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/inspec/rule.rb', line 32

def initialize(id, profile_id, opts, &block)
  @impact = nil
  @title = nil
  @desc = nil
  @refs = []
  @tags = {}

  # not changeable by the user:
  @__block = block
  @__source_location = __get_block_source_location(&block)
  @__rule_id = id
  @__profile_id = profile_id
  @__checks = []
  @__skip_rule = nil
  @__merge_count = 0
  @__skip_only_if_eval = opts[:skip_only_if_eval]

  # evaluate the given definition
  return unless block_given?
  begin
    instance_eval(&block)
  rescue StandardError => e
    # We've encountered an exception while trying to eval the code inside the
    # control block. We need to prevent the exception from bubbling up, and
    # fail the control. Controls are failed by having a failed resource within
    # them; but since our control block is unsafe (and opaque) to us, let's
    # make a dummy and fail that.
    location = block.source_location.compact.join(':')
    describe 'Control Source Code Error' do
      # Rubocop thinks we are raising an exception - we're actually calling RSpec's fail()
      its(location) { fail e.message } # rubocop: disable Style/SignalException
    end
  end
end

Class Method Details

.checks(rule) ⇒ Object



168
169
170
# File 'lib/inspec/rule.rb', line 168

def self.checks(rule)
  rule.instance_variable_get(:@__checks)
end

.merge(dst, src) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/inspec/rule.rb', line 196

def self.merge(dst, src)
  if src.id != dst.id
    # TODO: register an error, this case should not happen
    return
  end
  sp = rule_id(src)
  dp = rule_id(dst)
  if sp != dp
    # TODO: register an error, this case should not happen
    return
  end
  # merge all fields
  dst.impact(src.impact) unless src.impact.nil?
  dst.title(src.title)   unless src.title.nil?
  dst.desc(src.desc)     unless src.desc.nil?
  # merge indirect fields
  # checks defined in the source will completely eliminate
  # all checks that were defined in the destination
  sc = checks(src)
  dst.instance_variable_set(:@__checks, sc) unless sc.empty?
  sr = skip_status(src)
  set_skip_rule(dst, sr) unless sr.nil?
  # increment merge count
  dst.instance_variable_set(:@__merge_count, merge_count(dst) + 1)
end

.merge_count(rule) ⇒ Object



180
181
182
# File 'lib/inspec/rule.rb', line 180

def self.merge_count(rule)
  rule.instance_variable_get(:@__merge_count)
end

.prepare_checks(rule) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
# File 'lib/inspec/rule.rb', line 184

def self.prepare_checks(rule)
  msg = skip_status(rule)
  return checks(rule) unless msg
  msg = 'Skipped control due to only_if condition.' if msg == true

  # TODO: we use os as the carrier here, but should consider
  # a separate resource to do skipping
  resource = rule.os
  resource.skip_resource(msg)
  [['describe', [resource], nil]]
end

.profile_id(rule) ⇒ Object



164
165
166
# File 'lib/inspec/rule.rb', line 164

def self.profile_id(rule)
  rule.instance_variable_get(:@__profile_id)
end

.resource_dslObject

rubocop:disable Style/TrivialAccessors



28
29
30
# File 'lib/inspec/rule.rb', line 28

def self.resource_dsl # rubocop:disable Style/TrivialAccessors
  @resource_dsl
end

.rule_id(rule) ⇒ Object



156
157
158
# File 'lib/inspec/rule.rb', line 156

def self.rule_id(rule)
  rule.instance_variable_get(:@__rule_id)
end

.set_rule_id(rule, value) ⇒ Object



160
161
162
# File 'lib/inspec/rule.rb', line 160

def self.set_rule_id(rule, value)
  rule.instance_variable_set(:@__rule_id, value)
end

.set_skip_rule(rule, value) ⇒ Object



176
177
178
# File 'lib/inspec/rule.rb', line 176

def self.set_skip_rule(rule, value)
  rule.instance_variable_set(:@__skip_rule, value)
end

.skip_status(rule) ⇒ Object



172
173
174
# File 'lib/inspec/rule.rb', line 172

def self.skip_status(rule)
  rule.instance_variable_get(:@__skip_rule)
end

.with_resource_dsl(resource_dsl) ⇒ Object

Include any resources from the given resource DSL. The passed resource_dsl will also be included in any Inspec::Expect objects we make.



22
23
24
25
26
# File 'lib/inspec/rule.rb', line 22

def self.with_resource_dsl(resource_dsl)
  include resource_dsl
  @resource_dsl = resource_dsl
  true
end

Instance Method Details

#desc(v = nil) ⇒ Object



86
87
88
89
# File 'lib/inspec/rule.rb', line 86

def desc(v = nil)
  @desc = unindent(v) unless v.nil?
  @desc
end

#describe(*values, &block) ⇒ nil|DescribeBase

Describe will add one or more tests to this control. There is 2 ways of calling it:

describe resource do ... end

or

describe.one do ... end

Parameters:

  • Resource (any)

    to be describe, string, or nil

  • An (Proc)

    optional block containing tests for the described resource

Returns:

  • (nil|DescribeBase)

    if called without arguments, returns DescribeBase



139
140
141
142
143
144
145
146
147
148
# File 'lib/inspec/rule.rb', line 139

def describe(*values, &block)
  if values.empty? && !block_given?
    dsl = self.class.ancestors[1]
    Class.new(DescribeBase) do
      include dsl
    end.new(method(:__add_check))
  else
    __add_check('describe', values, with_dsl(block))
  end
end

#expect(value, &block) ⇒ Object



150
151
152
153
154
# File 'lib/inspec/rule.rb', line 150

def expect(value, &block)
  target = Inspec::Expect.new(value, &with_dsl(block))
  __add_check('expect', [value], target)
  target
end

#id(*_) ⇒ Object



71
72
73
74
# File 'lib/inspec/rule.rb', line 71

def id(*_)
  # never overwrite the ID
  @id
end

#impact(v = nil) ⇒ Object



76
77
78
79
# File 'lib/inspec/rule.rb', line 76

def impact(v = nil)
  @impact = v unless v.nil?
  @impact
end

#only_ifnil

Skip all checks if only_if is false

Parameters:

  • &block (Type)

    returns true if tests are added, false otherwise

Returns:

  • (nil)


120
121
122
123
124
125
# File 'lib/inspec/rule.rb', line 120

def only_if
  return unless block_given?
  return if @__skip_only_if_eval == true

  @__skip_rule ||= !yield
end

#ref(ref = nil, opts = {}) ⇒ Object



91
92
93
94
95
96
97
98
99
# File 'lib/inspec/rule.rb', line 91

def ref(ref = nil, opts = {})
  return @refs if ref.nil? && opts.empty?
  if opts.empty? && ref.is_a?(Hash)
    opts = ref
  else
    opts[:ref] = ref
  end
  @refs.push(opts)
end

#source_fileObject



112
113
114
# File 'lib/inspec/rule.rb', line 112

def source_file
  @__file
end

#tag(*args) ⇒ Object



101
102
103
104
105
106
107
108
109
110
# File 'lib/inspec/rule.rb', line 101

def tag(*args)
  args.each do |arg|
    if arg.is_a?(Hash)
      @tags.merge!(arg)
    else
      @tags[arg] ||= nil
    end
  end
  @tags
end

#title(v = nil) ⇒ Object



81
82
83
84
# File 'lib/inspec/rule.rb', line 81

def title(v = nil)
  @title = v unless v.nil?
  @title
end

#to_sObject



67
68
69
# File 'lib/inspec/rule.rb', line 67

def to_s
  Inspec::Rule.rule_id(self)
end