mirror of
https://github.com/inspec/inspec
synced 2024-11-23 05:03:07 +00:00
generate hash output for check and use it in inspec cli
This commit is contained in:
parent
6b7e5818fb
commit
1796c3271b
2 changed files with 95 additions and 31 deletions
57
bin/inspec
57
bin/inspec
|
@ -54,9 +54,9 @@ class InspecCLI < Thor # rubocop:disable Metrics/ClassLength
|
|||
desc: 'Save the created profile to a path'
|
||||
def json(path)
|
||||
diagnose
|
||||
|
||||
o = opts.dup
|
||||
o[:ignore_supports] = true
|
||||
|
||||
profile = Inspec::Profile.from_path(path, o)
|
||||
dst = o[:output].to_s
|
||||
if dst.empty?
|
||||
|
@ -75,21 +75,33 @@ class InspecCLI < Thor # rubocop:disable Metrics/ClassLength
|
|||
desc 'check PATH', 'verify all tests at the specified PATH'
|
||||
option :format, type: :string
|
||||
def check(path)
|
||||
diagnose
|
||||
o = opts.dup
|
||||
# configure_logger(o) # we do not need a logger for check yet
|
||||
o[:ignore_supports] = true # we check for integrity only
|
||||
o[:logger] = Logger.new(STDOUT)
|
||||
o[:logger].level = get_log_level(o.log_level)
|
||||
|
||||
# output json if we have activated the json formatter
|
||||
if opts['format'] == 'json'
|
||||
o[:logger].formatter = Logger::JSONFormatter.new
|
||||
else
|
||||
diagnose
|
||||
end
|
||||
|
||||
# run check
|
||||
profile = Inspec::Profile.from_path(path, o)
|
||||
exit 1 unless profile.check
|
||||
success, result = profile.check
|
||||
|
||||
if opts['format'] == 'json'
|
||||
puts JSON.generate(result)
|
||||
else
|
||||
headline('Summary')
|
||||
%w{location profile controls timestamp valid}.each { |item|
|
||||
puts "#{mark_text(item.to_s.capitalize + ':')} #{result[:summary][item.to_sym]}"
|
||||
}
|
||||
newline
|
||||
|
||||
%w{errors warnings}.each { |list|
|
||||
headline(list.to_s.capitalize)
|
||||
result[list.to_sym].each { |item|
|
||||
puts "#{item[:file]}:#{item[:line]}:#{item[:column]}: #{item[:msg]} "
|
||||
}
|
||||
newline
|
||||
}
|
||||
end
|
||||
exit 1 unless success
|
||||
end
|
||||
|
||||
desc 'archive PATH', 'archive a profile to tar.gz (default) or zip'
|
||||
|
@ -188,6 +200,15 @@ class InspecCLI < Thor # rubocop:disable Metrics/ClassLength
|
|||
@json ||= conffile ? read_config(conffile) : {}
|
||||
end
|
||||
|
||||
def configure_logger(o)
|
||||
o[:logger] = Logger.new(STDOUT)
|
||||
# output json if we have activated the json formatter
|
||||
if opts['log-format'] == 'json'
|
||||
o[:logger].formatter = Logger::JSONFormatter.new
|
||||
end
|
||||
o[:logger].level = get_log_level(o.log_level)
|
||||
end
|
||||
|
||||
# get the log level
|
||||
# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
|
||||
def get_log_level(level)
|
||||
|
@ -215,5 +236,19 @@ class InspecCLI < Thor # rubocop:disable Metrics/ClassLength
|
|||
puts "Failed to load JSON configuration: #{e}\nConfig was: #{config.inspect}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def mark_text(text)
|
||||
"\e[0;32m#{text}\e[0m"
|
||||
end
|
||||
|
||||
def headline(title)
|
||||
puts title
|
||||
title.each_char {|c| print '-' }
|
||||
newline
|
||||
end
|
||||
|
||||
def newline
|
||||
print "\n"
|
||||
end
|
||||
end
|
||||
InspecCLI.start(ARGV)
|
||||
|
|
|
@ -55,6 +55,7 @@ module Inspec
|
|||
impact: rule.impact,
|
||||
checks: rule.instance_variable_get(:@checks),
|
||||
code: rule.instance_variable_get(:@__code),
|
||||
source_location: rule.instance_variable_get(:@__source_location),
|
||||
group_title: rule.instance_variable_get(:@__group_title),
|
||||
}
|
||||
end
|
||||
|
@ -89,32 +90,59 @@ module Inspec
|
|||
#
|
||||
# @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
|
||||
|
||||
# initial values for response object
|
||||
result = {
|
||||
:summary => {
|
||||
:valid => false,
|
||||
:timestamp => Time.now.iso8601,
|
||||
:location => @path,
|
||||
:profile => nil,
|
||||
:controls => 0,
|
||||
},
|
||||
:errors => [],
|
||||
:warnings => [],
|
||||
}
|
||||
error = lambda { |msg|
|
||||
|
||||
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)
|
||||
no_warnings = no_errors = false
|
||||
result[:errors].push(entry.call(file, line, column, control, msg))
|
||||
}
|
||||
|
||||
@logger.info "Checking profile in #{@path}"
|
||||
|
||||
@logger.info 'Metadata OK.' if @metadata.valid?
|
||||
|
||||
if @content.any? { |h| h[:type] == :metadata && h[:ref] =~ /metadata\.rb$/ }
|
||||
warn.call('The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
|
||||
warn.call(Pathname.new(path).join('metadata.rb'), 0,0,nil, 'The use of `metadata.rb` is deprecated. Use `inspec.yml`.')
|
||||
end
|
||||
|
||||
@logger.info 'Metadata OK.' if @metadata.valid?
|
||||
result[:summary][:profile] = @metadata.params[:name]
|
||||
|
||||
# check if the profile is using the old test directory instead of the
|
||||
# new controls directory
|
||||
if @content.any? { |h| h[:type] == :test && h[:ref] =~ %r{test/[^/]+$} }
|
||||
warn.call('Profile uses deprecated `test` directory, rename it to `controls`.')
|
||||
warn.call(Pathname.new(path).join('test'), 0,0,nil, 'Profile uses deprecated `test` directory, rename it to `controls`.')
|
||||
end
|
||||
|
||||
count = rules_count
|
||||
result[:summary][:controls] = count
|
||||
if count == 0
|
||||
warn.call('No controls or tests were defined.')
|
||||
warn.call(nil, nil, nil, nil, 'No controls or tests were defined.')
|
||||
else
|
||||
@logger.info("Found #{count} controls.")
|
||||
end
|
||||
|
@ -123,18 +151,19 @@ module Inspec
|
|||
@params[:rules].each { |group, controls|
|
||||
@logger.info "Verify all controls in #{group}"
|
||||
controls.each { |id, control|
|
||||
error.call('Avoid controls with empty IDs') if id.nil? or id.empty?
|
||||
sfile, sline = control[:source_location]
|
||||
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("Control #{id} has no title") if control[:title].to_s.empty?
|
||||
warn.call("Control #{id} has no description") if control[:desc].to_s.empty?
|
||||
warn.call("Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0
|
||||
warn.call("Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0
|
||||
warn.call("Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty?
|
||||
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?
|
||||
}
|
||||
}
|
||||
|
||||
@logger.info 'Control definitions OK.' if no_warnings
|
||||
no_errors
|
||||
@logger.info 'Control definitions OK.' if result[:warnings].empty?
|
||||
[result[:errors].empty?, result]
|
||||
end
|
||||
|
||||
def rules_count
|
||||
|
|
Loading…
Reference in a new issue