inspec/lib/inspec/profile.rb
2015-11-02 17:31:56 +01:00

138 lines
4.4 KiB
Ruby

# encoding: utf-8
# Copyright 2015 Dominik Richter. All rights reserved.
# author: Dominik Richter
# author: Christoph Hartmann
require 'inspec/metadata'
module Inspec
class Profile # rubocop:disable Metrics/ClassLength
def self.from_path(path, options = nil)
opt = {}
options.each { |k, v| opt[k.to_sym] = v } unless options.nil?
opt[:path] = path
Profile.new(opt)
end
attr_reader :params
attr_reader :metadata
def initialize(options = nil)
@options = options || {}
@profile_id = options[:id] || nil
@params = {}
@logger = options[:logger] || Logger.new(nil)
@path = @options[:path]
fail 'Cannot read an empty path.' if @path.nil? || @path.empty?
fail "Cannot find directory #{@path}" unless File.directory?(@path)
@metadata = read_metadata
@params = @metadata.params unless @metadata.nil?
@params[:rules] = rules = {}
@runner = Runner.new(
id: @profile_id,
backend: :mock,
)
@runner.add_tests([@path])
@runner.rules.each do |id, rule|
file = rule.instance_variable_get(:@__file)
rules[file] ||= {}
rules[file][id] = {
title: rule.title,
desc: rule.desc,
impact: rule.impact,
checks: rule.instance_variable_get(:@checks),
code: rule.instance_variable_get(:@__code),
group_title: rule.instance_variable_get(:@__group_title),
}
end
end
def info
res = @params.dup
rules = {}
res[:rules].each do |gid, group|
next if gid.to_s.empty?
path = gid.sub(File.join(@path, ''), '')
rules[path] = { title: path, rules: {} }
group.each do |id, rule|
next if id.to_s.empty?
data = rule.dup
data.delete(:checks)
data[:impact] ||= 0.5
data[:impact] = 1.0 if data[:impact] > 1.0
data[:impact] = 0.0 if data[:impact] < 0.0
rules[path][:rules][id] = data
# TODO: temporarily flatten the group down; replace this with
# proper hierarchy later on
rules[path][:title] = data[:group_title]
end
end
res[:rules] = rules
res
end
# Check if the profile is internall well-structured. The logger will be
# used to print information on errors and warnings which are found.
#
# @return [Boolean] true if no errors were found, false otherwise
def check # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
no_errors = true
no_warnings = true
warn = lambda { |msg|
@logger.warn(msg)
no_warnings = false
}
error = lambda { |msg|
@logger.error(msg)
no_warnings = no_errors = false
}
@logger.info "Checking profile in #{@path}"
if @params[:name].to_s.empty?
error.call('No profile name defined')
elsif !(@params[:name].to_s =~ %r{^\S+\/\S+$})
error.call('Profile name must be defined as: OWNER/ID')
end
warn.call('No version defined') if @params[:name].to_s.empty?
warn.call('No title defined') if @params[:name].to_s.empty?
warn.call('No maintainer defined') if @params[:name].to_s.empty?
warn.call('No supports defined') if @params[:name].empty?
@logger.info 'Metadata OK.' if no_warnings
no_warnings = true
if @params[:name].empty?
warn.call('No rules were found.')
else
@logger.debug "Found #{@params[:name].length} rules."
end
# iterate over hash of groups
@params[:rules].each do |group, rules_array|
@logger.debug "Verify all rules in #{group}"
rules_array.each do |id, rule|
error.call('Avoid rules with empty IDs') if id.nil? or id.empty?
warn.call("Rule #{id} has no title") if rule[:title].to_s.empty?
warn.call("Rule #{id} has no description") if rule[:desc].to_s.empty?
warn.call("Rule #{id} has impact > 1.0") if rule[:impact].to_f > 1.0
warn.call("Rule #{id} has impact < 0.0") if rule[:impact].to_f < 0.0
warn.call("Rule #{id} has no tests defined") if rule[:checks].nil? or rule[:checks].empty?
end
end
@logger.info 'Rule definitions OK.' if no_warnings
no_errors
end
private
def read_metadata
mpath = File.join(@path, 'metadata.rb')
@metadata = Metadata.from_file(mpath, @profile_id, @logger)
end
end
end