mirror of
https://github.com/inspec/inspec
synced 2024-11-26 22:50:36 +00:00
Add a merged json report for A2 (#3261)
* Provide a json_merge report used by A2 that merges all child profiles. Signed-off-by: Jared Quick <jquick@chef.io> * Merge profile controls from child up until we find something usable. Signed-off-by: Jared Quick <jquick@chef.io> * Add testng for json_merged report. Signed-off-by: Jared Quick <jquick@chef.io> * Push the profile population to be later in the report. Signed-off-by: Jared Quick <jquick@chef.io>
This commit is contained in:
parent
0068683601
commit
73a40139a6
6 changed files with 141 additions and 2 deletions
|
@ -1,6 +1,7 @@
|
|||
require 'inspec/reporters/base'
|
||||
require 'inspec/reporters/cli'
|
||||
require 'inspec/reporters/json'
|
||||
require 'inspec/reporters/json_merged'
|
||||
require 'inspec/reporters/json_min'
|
||||
require 'inspec/reporters/junit'
|
||||
require 'inspec/reporters/automate'
|
||||
|
|
|
@ -4,7 +4,7 @@ require 'json'
|
|||
require 'net/http'
|
||||
|
||||
module Inspec::Reporters
|
||||
class Automate < Json
|
||||
class Automate < JsonMerged
|
||||
def initialize(config)
|
||||
super(config)
|
||||
|
||||
|
@ -17,7 +17,7 @@ module Inspec::Reporters
|
|||
|
||||
def enriched_report
|
||||
# grab the report from the parent class
|
||||
final_report = report
|
||||
final_report = report_merged
|
||||
|
||||
# Label this content as an inspec_report
|
||||
final_report[:type] = 'inspec_report'
|
||||
|
|
73
lib/inspec/reporters/json_merged.rb
Normal file
73
lib/inspec/reporters/json_merged.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'json'
|
||||
|
||||
module Inspec::Reporters
|
||||
class JsonMerged < Json
|
||||
def initialize(config)
|
||||
super(config)
|
||||
@profiles = []
|
||||
end
|
||||
|
||||
def render
|
||||
output(report_merged.to_json, false)
|
||||
end
|
||||
|
||||
def report_merged
|
||||
# grab profiles from the json parent class
|
||||
@profiles = report[:profiles]
|
||||
|
||||
{
|
||||
platform: platform,
|
||||
profiles: merge_profiles,
|
||||
statistics: {
|
||||
duration: run_data[:statistics][:duration],
|
||||
},
|
||||
version: run_data[:version],
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_profiles
|
||||
@profiles.each do |profile|
|
||||
next unless profile.key?(:parent_profile)
|
||||
parent_profile = find_master_parent(profile)
|
||||
merge_controls(parent_profile, profile)
|
||||
merge_depends(parent_profile, profile)
|
||||
end
|
||||
|
||||
# delete child profiles
|
||||
@profiles.delete_if { |p| p.key?(:parent_profile) }
|
||||
|
||||
@profiles
|
||||
end
|
||||
|
||||
def find_master_parent(profile)
|
||||
return profile unless profile.key?(:parent_profile)
|
||||
|
||||
parent_profile = @profiles.select { |parent| parent[:name] == profile[:parent_profile] }.first
|
||||
find_master_parent(parent_profile)
|
||||
end
|
||||
|
||||
def merge_controls(parent, child)
|
||||
parent[:controls].each do |control|
|
||||
child_control = child[:controls].select { |c| c[:id] == control[:id] }.first
|
||||
next if child_control.nil?
|
||||
|
||||
control.each do |name, _value|
|
||||
child_value = child_control[name]
|
||||
next if child_value.nil? || (child_value.respond_to?(:empty?) && child_value.empty?)
|
||||
control[name] = child_value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def merge_depends(parent, child)
|
||||
return unless child.key?(:depends)
|
||||
child[:depends].each do |d|
|
||||
parent[:depends] << d
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
1
test/unit/mock/reporters/json_merged_output
Normal file
1
test/unit/mock/reporters/json_merged_output
Normal file
|
@ -0,0 +1 @@
|
|||
{"platform":{"name":"mac_os_x","release":"17.5.0"},"profiles":[{"name":"wrapper-override","version":"0.6.1","sha256":"7436aac31d44de7987419d5f2ffb822f265645f4fc3c5d2ab37d8fff4dd5cf61","title":"Linux Wrapper Child Profile","maintainer":"Demo, Inc.","summary":"Profile that wraps other profiles","license":"Apache-2.0","copyright":"Demo, Inc.","copyright_email":"support@example.com","supports":[],"attributes":[],"depends":[{"name":"myprofile1z","url":"https://s3-eu-west-1.amazonaws.com/apop-bucket/profiles/myprofile1-1.0.0.tar.gz"}],"groups":[{"id":"/Users/jquick/.inspec/cache/e39eb85366b272bae98e5eecdfac9f84c50a9ae9dd625fba2ce847268a6c3477/controls/profile1.rb","controls":["pro1-con1","pro1-con2","pro1-con4"]}],"controls":[{"id":"pro1-con1","title":"Profile 1 - Control 1","desc":"Profile 1 - Control 1 description","impact":0.8,"refs":[],"tags":{"hosts":null,"file":null,"cce":"CCE-27072-8"},"code":"control 'pro1-con1' do\n impact 0.8\n title 'Profile 1 - Control 1'\n desc 'Profile 1 - Control 1 description'\n tag 'hosts','file'\n tag cce: 'CCE-27072-8'\n describe file('/etc/hosts') do\n its('mode') { should eq 0644 }\n end\nend\n","source_location":{"line":1,"ref":"/Users/jquick/.inspec/cache/e39eb85366b272bae98e5eecdfac9f84c50a9ae9dd625fba2ce847268a6c3477/controls/profile1.rb"},"results":[{"status":"passed","code_desc":"File /etc/hosts mode should eq 420","run_time":0.031503,"start_time":"2018-07-30T08:56:41-04:00"}]},{"id":"pro1-con2","title":"Profile 1 - Control 2-updated","desc":"Profile 1 - Control 2 description-updated","impact":0.999,"refs":[{"ref":[{"url":"https://example.com","ref":"Section 3.5.2.1"}]}],"tags":{"password":null,"password-updated":null},"code":" control 'pro1-con2' do\n impact 0.999\n title 'Profile 1 - Control 2-updated'\n desc 'Profile 1 - Control 2 description-updated'\n tag 'password-updated'\n ref 'Section 3.5.2.1', url: 'https://example.com'\n describe file('/etc/passwd') do\n it { should exist }\n end\n end\n","source_location":{"line":6,"ref":"wrapper-override/controls/defaut.rb"},"results":[{"status":"passed","code_desc":"File /etc/passwd should exist","run_time":0.003954,"start_time":"2018-07-30T08:56:41-04:00"}]},{"id":"pro1-con4","title":"Profile 1 - Control 3 - useless","desc":"Profile 1 - Control 3 description","impact":1,"refs":[],"tags":{},"code":"control 'pro1-con4' do\n impact 1\n title 'Profile 1 - Control 3 - useless'\n desc 'Profile 1 - Control 3 description'\n only_if do\n 1.eql?(0)\n end\n describe file('/tmp5') do\n it { should exist }\n end\nend\n","source_location":{"line":31,"ref":"/Users/jquick/.inspec/cache/e39eb85366b272bae98e5eecdfac9f84c50a9ae9dd625fba2ce847268a6c3477/controls/profile1.rb"},"results":[{"status":"skipped","code_desc":"Operating System Detection","run_time":2.9e-05,"start_time":"2018-07-30T08:56:41-04:00","resource":"Operating System Detection","skip_message":"Skipped control due to only_if condition."}]}]}],"statistics":{"duration":0.039182},"version":"2.2.26"}
|
1
test/unit/mock/reporters/run_data_wrapper.json
Normal file
1
test/unit/mock/reporters/run_data_wrapper.json
Normal file
File diff suppressed because one or more lines are too long
63
test/unit/reporters/json_merged.rb
Normal file
63
test/unit/reporters/json_merged.rb
Normal file
|
@ -0,0 +1,63 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'helper'
|
||||
|
||||
describe Inspec::Reporters::JsonMin do
|
||||
let(:path) { File.expand_path(File.dirname(__FILE__)) }
|
||||
let(:report) do
|
||||
data = JSON.parse(File.read(path + '/../mock/reporters/run_data_wrapper.json'), symbolize_names: true)
|
||||
Inspec::Reporters::JsonMerged.new({ run_data: data })
|
||||
end
|
||||
let(:profiles) { report.report[:profiles] }
|
||||
|
||||
describe '#render' do
|
||||
it 'confirms render output' do
|
||||
output = File.read(path + '/../mock/reporters/json_merged_output')
|
||||
report.render
|
||||
report.rendered_output.must_equal output
|
||||
end
|
||||
end
|
||||
|
||||
describe '#report_merged' do
|
||||
it 'outputs the correct report_merged' do
|
||||
output = File.read(path + '/../mock/reporters/json_merged_output')
|
||||
output = JSON.parse(output, symbolize_names: true)
|
||||
report.report_merged.must_equal output
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_master_parent' do
|
||||
it 'finds the parent' do
|
||||
report.instance_variable_set(:@profiles, profiles)
|
||||
parent = report.send(:find_master_parent, profiles[1])
|
||||
parent[:name].must_equal 'wrapper-override'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#merge_controls' do
|
||||
it 'merges profile controls' do
|
||||
parent = profiles[0]
|
||||
child = profiles[1]
|
||||
parent[:controls].select { |c| c[:id] == 'pro1-con4' }.first[:code].must_equal ''
|
||||
report.send(:merge_controls, parent, child)
|
||||
assert = "control 'pro1-con4' do\n impact 1\n title 'Profile 1 - Control 3 - useless'\n desc 'Profile 1 - Control 3 description'\n only_if do\n 1.eql?(0)\n end\n describe file('/tmp5') do\n it { should exist }\n end\nend\n"
|
||||
parent[:controls].select { |c| c[:id] == 'pro1-con4' }.first[:code].must_equal assert
|
||||
end
|
||||
end
|
||||
|
||||
describe '#merge_depends' do
|
||||
it 'merges profile depends' do
|
||||
parent = profiles[0]
|
||||
child = profiles[1]
|
||||
child[:depends] = [{:name=>"myprofile2", :url=>"https://test/myprofile2-1.0.0.tar.gz"}]
|
||||
assert = [{:name=>"myprofile1z", :url=>"https://s3-eu-west-1.amazonaws.com/apop-bucket/profiles/myprofile1-1.0.0.tar.gz"}]
|
||||
parent[:depends].must_equal assert
|
||||
report.send(:merge_depends, parent, child)
|
||||
assert = [
|
||||
{:name=>"myprofile1z", :url=>"https://s3-eu-west-1.amazonaws.com/apop-bucket/profiles/myprofile1-1.0.0.tar.gz"},
|
||||
{:name=>"myprofile2", :url=>"https://test/myprofile2-1.0.0.tar.gz"},
|
||||
]
|
||||
parent[:depends].must_equal assert
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue