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.



31
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
66
67
# File 'lib/inspec/rule.rb', line 31

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

  # not changeable by the user:
  @__code = nil
  @__block = block
  @__source_location = __get_block_source_location(&block)
  @__rule_id = id
  @__profile_id = profile_id
  @__checks = []
  @__skip_rule = {}
  @__merge_count = 0
  @__merge_changes = []
  @__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



188
189
190
# File 'lib/inspec/rule.rb', line 188

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

.merge(dst, src) ⇒ Object

rubocop:disable Metrics/AbcSize



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/inspec/rule.rb', line 226

def self.merge(dst, src) # rubocop:disable Metrics/AbcSize
  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.descriptions(src.descriptions)     unless src.descriptions.nil?
  dst.tag(src.tag)                       unless src.tag.nil?
  dst.ref(src.ref)                       unless src.ref.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?
  skip_check = skip_status(src)
  sr = skip_check[:result]
  msg = skip_check[:message]
  set_skip_rule(dst, sr, msg) unless sr.nil?

  # Save merge history
  dst.instance_variable_set(:@__merge_count, merge_count(dst) + 1)
  dst.instance_variable_set(
    :@__merge_changes,
    merge_changes(dst) << src.instance_variable_get(:@__source_location)
  )
end

.merge_changes(rule) ⇒ Object



205
206
207
# File 'lib/inspec/rule.rb', line 205

def self.merge_changes(rule)
  rule.instance_variable_get(:@__merge_changes)
end

.merge_count(rule) ⇒ Object



201
202
203
# File 'lib/inspec/rule.rb', line 201

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

.prepare_checks(rule) ⇒ Object



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/inspec/rule.rb', line 209

def self.prepare_checks(rule)
  skip_check = skip_status(rule)
  return checks(rule) unless skip_check[:result].eql?(true)

  if skip_check[:message]
    msg = "Skipped control due to only_if condition: #{skip_check[:message]}"
  else
    msg = "Skipped control due to only_if condition."
  end

  # 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



184
185
186
# File 'lib/inspec/rule.rb', line 184

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

.resource_dslObject

rubocop:disable Style/TrivialAccessors



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

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

.rule_id(rule) ⇒ Object



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

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

.set_rule_id(rule, value) ⇒ Object



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

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

.set_skip_rule(rule, value, message = nil) ⇒ Object



196
197
198
199
# File 'lib/inspec/rule.rb', line 196

def self.set_skip_rule(rule, value, message = nil)
  rule.instance_variable_set(:@__skip_rule,
    { result: value, message: message })
end

.skip_status(rule) ⇒ Object



192
193
194
# File 'lib/inspec/rule.rb', line 192

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.



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

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

Instance Method Details

#desc(v = nil, data = nil) ⇒ Object



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

def desc(v = nil, data = nil)
  return @descriptions[:default] if v.nil?

  if data.nil?
    @descriptions[:default] = unindent(v)
  else
    @descriptions[v.to_sym] = unindent(data)
  end
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



159
160
161
162
163
164
165
166
167
168
# File 'lib/inspec/rule.rb', line 159

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

#descriptions(description_hash = nil) ⇒ Object



103
104
105
106
107
# File 'lib/inspec/rule.rb', line 103

def descriptions(description_hash = nil)
  return @descriptions if description_hash.nil?

  @descriptions.merge!(description_hash)
end

#expect(value, &block) ⇒ Object



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

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

#id(*_) ⇒ Object



73
74
75
76
# File 'lib/inspec/rule.rb', line 73

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

#impact(v = nil) ⇒ Object



78
79
80
81
82
83
84
85
86
# File 'lib/inspec/rule.rb', line 78

def impact(v = nil)
  if v.is_a?(String)
    @impact = Inspec::Impact.impact_from_string(v)
  elsif !v.nil?
    @impact = v
  end

  @impact
end

#only_if(message = nil) ⇒ nil

Skip all checks if only_if is false

Parameters:

  • &block (Type)

    returns true if tests are added, false otherwise

Returns:

  • (nil)


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

def only_if(message = nil)
  return unless block_given?
  return if @__skip_only_if_eval == true

  @__skip_rule[:result] ||= !yield
  @__skip_rule[:message] = message
end

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



109
110
111
112
113
114
115
116
117
118
# File 'lib/inspec/rule.rb', line 109

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



131
132
133
# File 'lib/inspec/rule.rb', line 131

def source_file
  @__file
end

#tag(*args) ⇒ Object



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

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



88
89
90
91
# File 'lib/inspec/rule.rb', line 88

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

#to_sObject



69
70
71
# File 'lib/inspec/rule.rb', line 69

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