class InspecTools::Summary

Attributes

json[R]
json_counts[R]
json_full[R]
summary[R]
threshold[R]
threshold_file[R]
threshold_inline[R]

Public Class Methods

new(**options) click to toggle source
# File lib/inspec_tools/summary.rb, line 20
def initialize(**options)
  options = options[:options]
  @json = JSON.parse(File.read(options[:inspec_json]))
  @json_full = false || options[:json_full]
  @json_counts = false || options[:json_counts]
  @threshold = parse_threshold(options[:threshold_inline], options[:threshold_file])
  @threshold_provided = options[:threshold_inline] || options[:threshold_file]
  @summary = compute_summary
end

Public Instance Methods

output_summary() click to toggle source
# File lib/inspec_tools/summary.rb, line 30
def output_summary
  unless @json_full || @json_counts
    puts "\nThreshold compliance: #{@threshold['compliance.min']}%"
    puts "\nOverall compliance: #{@summary[:compliance]}%\n\n"
    @summary[:status].keys.each do |category|
      puts category
      @summary[:status][category].keys.each do |impact|
        puts "\t#{impact} : #{@summary[:status][category][impact]}"
      end
    end
  end

  puts @summary.to_json if @json_full
  puts @summary[:status].to_json if @json_counts
end
results_meet_threshold?() click to toggle source
# File lib/inspec_tools/summary.rb, line 46
def results_meet_threshold?
  raise 'Please provide threshold as a yaml file or inline yaml' unless @threshold_provided

  compliance = true
  failure = []
  failure << check_max_compliance(@threshold['compliance.max'], @summary[:compliance], '', 'compliance')
  failure << check_min_compliance(@threshold['compliance.min'], @summary[:compliance], '', 'compliance')

  BUCKETS.each do |bucket|
    TALLYS.each do |tally|
      failure << check_min_compliance(@threshold["#{bucket}.#{tally}.min"], @summary[:status][bucket][tally], bucket, tally)
      failure << check_max_compliance(@threshold["#{bucket}.#{tally}.max"], @summary[:status][bucket][tally], bucket, tally)
    end
  end

  failure.reject!(&:nil?)
  compliance = false if failure.length.positive?
  output(compliance, failure)
  compliance
end

Private Instance Methods

check_max_compliance(max, data, bucket, tally) click to toggle source
# File lib/inspec_tools/summary.rb, line 73
def check_max_compliance(max, data, bucket, tally)
  expected_to_string(bucket, tally, 'max', max, data) if max != -1 and data > max
end
check_min_compliance(min, data, bucket, tally) click to toggle source
# File lib/inspec_tools/summary.rb, line 69
def check_min_compliance(min, data, bucket, tally)
  expected_to_string(bucket, tally, 'min', min, data) if min != -1 and data < min
end
compute_compliance(summary) click to toggle source
# File lib/inspec_tools/summary.rb, line 142
def compute_compliance(summary)
  (summary[:status][:passed][:total]*100.0/
    (summary[:status][:passed][:total]+
     summary[:status][:failed][:total]+
     summary[:status][:skipped][:total]+
     summary[:status][:error][:total])).floor
end
compute_summary() click to toggle source
# File lib/inspec_tools/summary.rb, line 99
def compute_summary
  data = Utils::InspecUtil.parse_data_for_ckl(@json)

  data.keys.each do |control_id|
    current_control = data[control_id]
    current_control[:compliance_status] = Utils::InspecUtil.control_status(current_control, true)
    current_control[:finding_details] = Utils::InspecUtil.control_finding_details(current_control, current_control[:compliance_status])
  end

  summary = {}
  summary[:buckets] = {}
  summary[:buckets][:failed]    = select_by_status(data, 'Open')
  summary[:buckets][:passed]    = select_by_status(data, 'NotAFinding')
  summary[:buckets][:no_impact] = select_by_status(data, 'Not_Applicable')
  summary[:buckets][:skipped]   = select_by_status(data, 'Not_Reviewed')
  summary[:buckets][:error]     = select_by_status(data, 'Profile_Error')

  summary[:status] = {}
  %i(failed passed no_impact skipped error).each do |key|
    summary[:status][key] = tally_by_impact(summary[:buckets][key])
  end
  summary[:compliance] = compute_compliance(summary)
  summary
end
expected_to_string(bucket, tally, maxmin, value, got) click to toggle source
# File lib/inspec_tools/summary.rb, line 86
def expected_to_string(bucket, tally, maxmin, value, got)
  return "Expected #{bucket}.#{tally}.#{maxmin}:#{value} got:#{got}" unless bucket.empty? || bucket.nil?

  "Expected #{tally}.#{maxmin}:#{value}\% got:#{got}\%"
end
output(passed_threshold, what_failed) click to toggle source
# File lib/inspec_tools/summary.rb, line 77
def output(passed_threshold, what_failed)
  if passed_threshold
    puts "Overall compliance threshold of #{@threshold['compliance.min']}\% met. Current compliance at #{@summary[:compliance]}\%"
  else
    puts 'Compliance threshold was not met: '
    puts what_failed.join("\n")
  end
end
parse_threshold(threshold_inline, threshold_file) click to toggle source
# File lib/inspec_tools/summary.rb, line 92
def parse_threshold(threshold_inline, threshold_file)
  threshold = Utils::InspecUtil.to_dotted_hash(YAML.load_file(THRESHOLD_TEMPLATE))
  threshold.merge!(Utils::InspecUtil.to_dotted_hash(YAML.load_file(threshold_file))) if threshold_file
  threshold.merge!(Utils::InspecUtil.to_dotted_hash(YAML.safe_load(threshold_inline))) if threshold_inline
  threshold
end
select_by_impact(controls, impact) click to toggle source
# File lib/inspec_tools/summary.rb, line 124
def select_by_impact(controls, impact)
  controls.select { |_key, value| value[:impact].to_f.eql?(impact) }
end
select_by_status(controls, status) click to toggle source
# File lib/inspec_tools/summary.rb, line 128
def select_by_status(controls, status)
  controls.select { |_key, value| value[:compliance_status].eql?(status) }
end
tally_by_impact(controls) click to toggle source
# File lib/inspec_tools/summary.rb, line 132
def tally_by_impact(controls)
  tally = {}
  tally[:total]    = controls.count
  tally[:critical] = select_by_impact(controls, CRITICAL).count
  tally[:high]     = select_by_impact(controls, HIGH).count
  tally[:medium]   = select_by_impact(controls, MEDIUM).count
  tally[:low]      = select_by_impact(controls, LOW).count
  tally
end