Method: Inspec::Profile#check

Defined in:
lib/inspec/profile.rb

#checkBoolean

Check if the profile is internally well-structured. The logger will be used to print information on errors and warnings which are found.

Returns:

  • (Boolean)

    true if no errors were found, false otherwise



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
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'lib/inspec/profile.rb', line 237

def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
  # initial values for response object
  result = {
    summary: {
      valid: false,
      timestamp: Time.now.iso8601,
      location: @target,
      profile: nil,
      controls: 0,
    },
    errors: [],
    warnings: [],
  }

  entry = lambda { |file, line, column, control, msg|
    {
      file: file,
      line: line,
      column: column,
      control_id: control,
      msg: msg,
    }
  }

  warn = lambda { |file, line, column, control, msg|
    @logger.warn(msg)
    result[:warnings].push(entry.call(file, line, column, control, msg))
  }

  error = lambda { |file, line, column, control, msg|
    @logger.error(msg)
    result[:errors].push(entry.call(file, line, column, control, msg))
  }

  @logger.info "Checking profile in #{@target}"
  meta_path = @source_reader.target.abs_path(@source_reader..ref)
  if meta_path =~ /metadata\.rb$/
    warn.call(@target, 0, 0, nil, 'The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
  end

  # verify metadata
  m_errors, m_warnings = .valid
  m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) }
  m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) }
  m_unsupported = .unsupported
  m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") }
  @logger.info 'Metadata OK.' if m_errors.empty? && m_unsupported.empty?

  # extract profile name
  result[:summary][:profile] = .params[:name]

  # check if the profile is using the old test directory instead of the
  # new controls directory
  if @source_reader.tests.keys.any? { |x| x =~ %r{^test/$} }
    warn.call(@target, 0, 0, nil, 'Profile uses deprecated `test` directory, rename it to `controls`.')
  end

  count = controls_count
  result[:summary][:controls] = count
  if count == 0
    warn.call(nil, nil, nil, nil, 'No controls or tests were defined.')
  else
    @logger.info("Found #{count} controls.")
  end

  # iterate over hash of groups
  params[:controls].each { |id, control|
    sfile = control[:source_location][:ref]
    sline = control[:source_location][:line]
    error.call(sfile, sline, nil, id, 'Avoid controls with empty IDs') if id.nil? or id.empty?
    next if id.start_with? '(generated '
    warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty?
    warn.call(sfile, sline, nil, id, "Control #{id} has no description") if control[:desc].to_s.empty?
    warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
    warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
    warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty?
  }

  # profile is valid if we could not find any error
  result[:summary][:valid] = result[:errors].empty?

  @logger.info 'Control definitions OK.' if result[:warnings].empty?
  result
end