Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stick to plain ruby hash #219

Merged
merged 1 commit into from
May 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions files/default/handler/audit_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,16 @@ def call(opts, profiles)
r.delete(:controls)

# calculate statistics
stats = count_controls(JSON.parse(r[:profiles].to_json))
stats = count_controls(r[:profiles])

time = 0
time = r[:statistics][:duration] unless r[:statistics].nil?

# count controls
Chef::Log.info "Summary #{stats['total']} controls: #{stats['passed']['total']} successful, #{stats['failed']['total']} failures, #{stats['skipped']['total']} skipped in #{time} s"
Chef::Log.info "Summary #{stats[:total]} controls: #{stats[:passed][:total]} successful, #{stats[:failed][:total]} failures, #{stats[:skipped][:total]} skipped in #{time} s"
end

r.to_json
Chef::Log.debug "Audit Report #{r}"
r
else
Chef::Log.warn 'No audit tests are defined.'
{}
Expand All @@ -179,13 +179,12 @@ def gather_nodeinfo
end

# send InSpec report to the reporter (see libraries/reporters.rb)
def send_report(reporter, server, user, source_location, content)
def send_report(reporter, server, user, source_location, report)
Chef::Log.info "Reporting to #{reporter}"

# Set `insecure` here to avoid passing 6 aruguments to `AuditReport#send_report`
# See `cookstyle` Metrics/ParameterLists
insecure = node['audit']['insecure']
report = JSON.parse(content)

# TODO: harmonize reporter interface
if reporter == 'chef-visibility' || reporter == 'chef-automate'
Expand Down
52 changes: 26 additions & 26 deletions libraries/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,37 +142,37 @@ def get_reporters(audit)
# e.g. minor: 10, major: 15, critical: 25
def count_controls(profiles)
count = {
'total' => 0,
'passed' => {
'total' => 0,
total: 0,
passed: {
total: 0,
},
'skipped' => {
'total' => 0,
skipped: {
total: 0,
},
'failed' => {
'total' => 0,
'minor' => 0,
'major' => 0,
'critical' => 0,
failed: {
total: 0,
minor: 0,
major: 0,
critical: 0,
},
}
return count unless profiles.is_a?(Array)

profiles.each do |profile|
next unless profile && profile['controls'].is_a?(Array)
profile['controls'].each do |control|
count['total'] += 1
next unless profile && profile[:controls].is_a?(Array)
profile[:controls].each do |control|
count[:total] += 1
# ensure all impacts are float
control['impact'] = control['impact'].to_f
case control_status(control['results'])
control[:impact] = control[:impact].to_f
case control_status(control[:results])
when 'passed'
count['passed']['total'] += 1
count[:passed][:total] += 1
when 'skipped'
count['skipped']['total'] += 1
count[:skipped][:total] += 1
when 'failed'
count['failed']['total'] += 1
criticality = impact_to_s(control['impact'])
count['failed'][criticality] += 1 unless criticality.nil?
count[:failed][:total] += 1
criticality = impact_to_s(control[:impact])
count[:failed][criticality.to_sym] += 1 unless criticality.nil?
end
end
end
Expand All @@ -182,11 +182,11 @@ def count_controls(profiles)
# Returns a complince status string based on the passed/failed/skipped controls
def compliance_status(counts)
return 'unknown' unless counts.is_a?(Hash) &&
counts['failed'].is_a?(Hash) &&
counts['skipped'].is_a?(Hash)
if counts['failed']['total'] > 0
counts[:failed].is_a?(Hash) &&
counts[:skipped].is_a?(Hash)
if counts[:failed][:total] > 0
'failed'
elsif counts['total'] == counts['skipped']['total']
elsif counts[:total] == counts[:skipped][:total]
'skipped'
else
'passed'
Expand All @@ -210,8 +210,8 @@ def control_status(results)
return unless results.is_a?(Array)
status = 'passed'
results.each do |result|
return 'failed' if result['status'] == 'failed'
status = 'skipped' if result['status'] == 'skipped'
return 'failed' if result[:status] == 'failed'
status = 'skipped' if result[:status] == 'skipped'
end
status
end
Expand Down
62 changes: 31 additions & 31 deletions libraries/reporters/automate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def send_report(report)
return false
end

json_report = enriched_report(report)
json_report = enriched_report(report).to_json

unless json_report
Chef::Log.warn 'Something went wrong, report can\'t be nil'
Expand Down Expand Up @@ -75,27 +75,27 @@ def send_report(report)

# Some document stores like ElasticSearch don't like values that change type
# This function converts all profile attribute defaults to string and
# adds a 'type' key to store the original type
# adds a :type key to store the original type
def typed_attributes(profiles)
return profiles unless profiles.class == Array && !profiles.empty?
profiles.each { |profile|
next unless profile['attributes'].class == Array && !profile['attributes'].empty?
profile['attributes'].map { |attrib|
case attrib['options']['default'].class.to_s
next unless profile[:attributes].class == Array && !profile[:attributes].empty?
profile[:attributes].map { |attrib|
case attrib[:options][:default].class.to_s
when 'String'
attrib['options']['type'] = 'string'
attrib[:options][:type] = 'string'
when 'FalseClass'
attrib['options']['type'] = 'boolean'
attrib['options']['default'] = attrib['options']['default'].to_s
attrib[:options][:type] = 'boolean'
attrib[:options][:default] = attrib[:options][:default].to_s
when 'Fixnum'
attrib['options']['type'] = 'int'
attrib['options']['default'] = attrib['options']['default'].to_s
attrib[:options][:type] = 'int'
attrib[:options][:default] = attrib[:options][:default].to_s
when 'Float'
attrib['options']['type'] = 'float'
attrib['options']['default'] = attrib['options']['default'].to_s
attrib[:options][:type] = 'float'
attrib[:options][:default] = attrib[:options][:default].to_s
else
Chef::Log.warn "enriched_report: unsupported data type(#{attrib['options']['default'].class}) for attribute #{attrib['options']['name']}"
attrib['options']['type'] = 'unknown'
Chef::Log.warn "enriched_report: unsupported data type(#{attrib[:options][:default].class}) for attribute #{attrib[:options]['name']}"
attrib[:options][:type] = 'unknown'
end
}
}
Expand All @@ -109,31 +109,31 @@ def typed_attributes(profiles)
def enriched_report(content)
return nil unless content.is_a?(Hash)
final_report = {}
total_duration = content['statistics']['duration']
inspec_version = content['version']
total_duration = content[:statistics][:duration]
inspec_version = content[:version]

# strip the report to leave only the profiles
final_report['profiles'] = content['profiles']
final_report[:profiles] = content[:profiles]

# remove nil profiles if any
final_report['profiles'].select! { |p| p }
final_report[:profiles].select! { |p| p }

# set types for profile attributes
final_report['profiles'] = typed_attributes(final_report['profiles'])
final_report[:profiles] = typed_attributes(final_report[:profiles])

# add some additional fields to ease report parsing
final_report['event_type'] = 'inspec'
final_report['event_action'] = 'exec'
final_report['compliance_summary'] = count_controls(final_report['profiles'])
final_report['compliance_summary']['status'] = compliance_status(final_report['compliance_summary'])
final_report['compliance_summary']['node_name'] = @node_name
final_report['compliance_summary']['end_time'] = DateTime.now.iso8601
final_report['compliance_summary']['duration'] = total_duration
final_report['compliance_summary']['inspec_version'] = inspec_version
final_report['entity_uuid'] = @entity_uuid
final_report['run_id'] = @run_id
Chef::Log.info "Compliance Summary #{final_report['compliance_summary']}"
final_report.to_json
final_report[:event_type] = 'inspec'
final_report[:event_action] = 'exec'
final_report[:compliance_summary] = count_controls(final_report[:profiles])
final_report[:compliance_summary][:status] = compliance_status(final_report[:compliance_summary])
final_report[:compliance_summary][:node_name] = @node_name
final_report[:compliance_summary][:end_time] = DateTime.now.iso8601
final_report[:compliance_summary][:duration] = total_duration
final_report[:compliance_summary][:inspec_version] = inspec_version
final_report[:entity_uuid] = @entity_uuid
final_report[:run_id] = @run_id
Chef::Log.info "Compliance Summary #{final_report[:compliance_summary]}"
final_report
end
end
end
32 changes: 15 additions & 17 deletions libraries/reporters/compliance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def send_report(report)

min_report = transform(report)
json_report = enriched_report(min_report, @source_location)
req.body = json_report
req.body = json_report.to_json

# TODO: use secure option
uri = URI(@url)
Expand All @@ -50,7 +50,7 @@ def enriched_report(report, source_location)
blob = @node_info.dup

# extract profile names
profiles = report['controls'].collect { |control| control['profile_id'] }.uniq
profiles = report[:controls].collect { |control| control[:profile_id] }.uniq

# build report for chef compliance, it includes node data
blob[:reports] = {}
Expand All @@ -62,41 +62,39 @@ def enriched_report(report, source_location)
namespace = compliance_profiles.select { |entry| entry[:profile_id] == profile }
unless namespace.nil? && namespace.empty?
Chef::Log.debug "Namespace for #{profile} is #{namespace[0][:owner]}"
blob[:profiles][profile] = namespace[0][:owner]
blob[:reports][profile] = report.dup
blob[:profiles][profile.to_sym] = namespace[0][:owner]
blob[:reports][profile.to_sym] = report.dup
# filter controls by profile_id
blob[:reports][profile]['controls'] = blob[:reports][profile]['controls'].select { |control| control['profile_id'] == profile }
blob[:reports][profile.to_sym][:controls] = blob[:reports][profile.to_sym][:controls].select { |control| control[:profile_id] == profile }
else
Chef::Log.warn "Could not determine compliance namespace for #{profile}"
end
}

blob.to_json
blob
end

# transforms a full InSpec json report to a min InSpec json report
def transform(full_report)
min_report = {}
min_report['version'] = full_report[:version]
min_report[:version] = full_report[:version]

# iterate over each profile and control
min_report['controls'] = []
min_report[:controls] = []
full_report[:profiles].each { |profile|
min_report['controls'] += profile[:controls].map { |control|
min_report[:controls] += profile[:controls].map { |control|
control[:results].map { |result|
c = {}
c['id'] = control[:id]
c['profile_id'] = profile[:name]
c['status'] = result[:status]
c['code_desc'] = result[:code_desc]
c[:id] = control[:id]
c[:profile_id] = profile[:name]
c[:status] = result[:status]
c[:code_desc] = result[:code_desc]
c
}
}
}
min_report['controls'].flatten!
min_report['statistics'] = {
'duration' => full_report[:statistics][:duration],
}
min_report[:controls].flatten!
min_report[:statistics] = full_report[:statistics]
min_report
end

Expand Down
4 changes: 2 additions & 2 deletions libraries/reporters/cs_automate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def initialize(opts)
end

def send_report(report)
json_report = enriched_report(report)
automate_report = enriched_report(report)

if @insecure
Chef::Config[:verify_api_cert] = false
Expand All @@ -27,7 +27,7 @@ def send_report(report)
Chef::Log.info "Report to Chef Automate via Chef Server: #{@url}"
rest = Chef::ServerAPI.new(@url, Chef::Config)
with_http_rescue do
rest.post(@url, JSON.parse(json_report))
rest.post(@url, automate_report)
return true
end
false
Expand Down
4 changes: 2 additions & 2 deletions libraries/reporters/cs_compliance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module Reporter
class ChefServerCompliance < ChefCompliance
def send_report(report)
min_report = transform(report)
json_report = enriched_report(min_report, @source_location)
cc_report = enriched_report(min_report, @source_location)

# TODO: only disable if insecure option is set
Chef::Config[:verify_api_cert] = false
Expand All @@ -19,7 +19,7 @@ def send_report(report)
Chef::Log.info "Report to Chef Compliance via Chef Server: #{@url}"
rest = Chef::ServerAPI.new(@url, Chef::Config)
with_http_rescue do
rest.post(@url, JSON.parse(json_report))
rest.post(@url, cc_report)
return true
end
false
Expand Down
Loading