From 522a2fc934eee8b0141f0427f4c1226897c07f62 Mon Sep 17 00:00:00 2001 From: Scott Babcock Date: Fri, 12 Jul 2019 11:39:51 -0700 Subject: [PATCH 1/5] Implement 'audit enforcer' reporter Signed-off-by: Scott Babcock --- README.md | 10 ++++++++++ files/default/handler/audit_report.rb | 2 ++ libraries/reporters/audit-enforcer.rb | 23 +++++++++++++++++++++++ metadata.rb | 4 ++-- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 libraries/reporters/audit-enforcer.rb diff --git a/README.md b/README.md index b38c696a..ae88631b 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,16 @@ the Chef log: [2017-08-29T00:22:10+00:00] INFO: Report handlers complete ``` +#### Enforce compliance with executed profiles + +The `audit-enforcer` enables you to enforce compliance with executed profiles. If the system under test is determined to be non-compliant, this reporter will raise an error and fail the Chef Client run. To activate compliance enforcement, set the `reporter` attribute to 'audit-enforcer': + +```ruby +default['audit']['reporter'] = 'audit-enforcer' +``` + +Note that detection of non-compliance will immediately terminate the Chef Client run. If you specify [multiple reporters](#multiple-reporters), place the `audit-enforcer` at the end of the list to + #### Multiple Reporters To enable multiple reporters, simply define multiple reporters with all the necessary information diff --git a/files/default/handler/audit_report.rb b/files/default/handler/audit_report.rb index 11b0f705..e8e396b0 100644 --- a/files/default/handler/audit_report.rb +++ b/files/default/handler/audit_report.rb @@ -295,6 +295,8 @@ def send_report(reporter, server, user, source_location, report) path = node['audit']['json_file']['location'] Chef::Log.info "Writing report to #{path}" Reporter::JsonFile.new(file: path).send_report(report) + elsif reporter == 'audit-enforcer' + Reporter::AuditEnforcer.new.send_report(report) else Chef::Log.warn "#{reporter} is not a supported InSpec report collector" end diff --git a/libraries/reporters/audit-enforcer.rb b/libraries/reporters/audit-enforcer.rb new file mode 100644 index 00000000..8f2bcf58 --- /dev/null +++ b/libraries/reporters/audit-enforcer.rb @@ -0,0 +1,23 @@ +# encoding: utf-8 + +module Reporter + # + # Used to raise an error on conformance failure + # + class AuditEnforcer + def send_report(report) + # iterate over each profile and control + report[:profiles].each do |profile| + unless profile[:controls].nil? + profile[:controls].each do |control| + next if control[:results].nil? + control[:results].each do |result| + fail "Audit #{control[:id]} has failed. Aborting chef-client run." if result[:status] == 'failed' + end + end + end + end + true + end + end +end diff --git a/metadata.rb b/metadata.rb index 117c9e9c..a10ee06d 100644 --- a/metadata.rb +++ b/metadata.rb @@ -3,9 +3,9 @@ maintainer 'Chef Software, Inc.' maintainer_email 'cookbooks@chef.io' license 'Apache-2.0' -description 'Allows for fetching and executing compliance profiles, and reporting its results' +description 'Allows for fetching and executing compliance profiles, and reporting their results' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version '7.8.0' +version '7.9.0' source_url 'https://github.com/chef-cookbooks/audit' issues_url 'https://github.com/chef-cookbooks/audit/issues' From dc0e34d86b0b81d3c0cf357263900083a400f131 Mon Sep 17 00:00:00 2001 From: Scott Babcock Date: Fri, 12 Jul 2019 11:47:08 -0700 Subject: [PATCH 2/5] Finish incomplete README updates Signed-off-by: Scott Babcock --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae88631b..5b70620c 100644 --- a/README.md +++ b/README.md @@ -328,7 +328,7 @@ The `audit-enforcer` enables you to enforce compliance with executed profiles. I default['audit']['reporter'] = 'audit-enforcer' ``` -Note that detection of non-compliance will immediately terminate the Chef Client run. If you specify [multiple reporters](#multiple-reporters), place the `audit-enforcer` at the end of the list to +Note that detection of non-compliance will immediately terminate the Chef Client run. If you specify [multiple reporters](#multiple-reporters), place the `audit-enforcer` at the end of the list, allowing the other reporters to generate their output prior to run termination. #### Multiple Reporters From c984e1840b838338ca1a6172b6fb9132d434bb96 Mon Sep 17 00:00:00 2001 From: Scott Babcock Date: Fri, 12 Jul 2019 13:22:37 -0700 Subject: [PATCH 3/5] Resolve merge conflict Signed-off-by: Scott Babcock --- metadata.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metadata.rb b/metadata.rb index a10ee06d..001ebf95 100644 --- a/metadata.rb +++ b/metadata.rb @@ -5,7 +5,7 @@ license 'Apache-2.0' description 'Allows for fetching and executing compliance profiles, and reporting their results' long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version '7.9.0' +version '8.1.0' source_url 'https://github.com/chef-cookbooks/audit' issues_url 'https://github.com/chef-cookbooks/audit/issues' From 080e769f7ffa70870b2954a30f7a69d6f8858c08 Mon Sep 17 00:00:00 2001 From: Alex Pop Date: Mon, 15 Jul 2019 15:39:46 +0100 Subject: [PATCH 4/5] Add unit tests for AuditEnforcer and sort lint errors Signed-off-by: Alex Pop --- libraries/reporters/audit-enforcer.rb | 11 ++--- spec/unit/libraries/audit_enforcer_spec.rb | 54 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 spec/unit/libraries/audit_enforcer_spec.rb diff --git a/libraries/reporters/audit-enforcer.rb b/libraries/reporters/audit-enforcer.rb index 8f2bcf58..1cc632c9 100644 --- a/libraries/reporters/audit-enforcer.rb +++ b/libraries/reporters/audit-enforcer.rb @@ -8,12 +8,11 @@ class AuditEnforcer def send_report(report) # iterate over each profile and control report[:profiles].each do |profile| - unless profile[:controls].nil? - profile[:controls].each do |control| - next if control[:results].nil? - control[:results].each do |result| - fail "Audit #{control[:id]} has failed. Aborting chef-client run." if result[:status] == 'failed' - end + next if profile[:controls].nil? + profile[:controls].each do |control| + next if control[:results].nil? + control[:results].each do |result| + raise "Audit #{control[:id]} has failed. Aborting chef-client run." if result[:status] == 'failed' end end end diff --git a/spec/unit/libraries/audit_enforcer_spec.rb b/spec/unit/libraries/audit_enforcer_spec.rb new file mode 100644 index 00000000..fd9748bd --- /dev/null +++ b/spec/unit/libraries/audit_enforcer_spec.rb @@ -0,0 +1,54 @@ +# encoding: utf-8 +# +# Cookbook Name:: audit +# Spec:: automate_spec +# +# Copyright 2016 Chef Software, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require_relative '../../../libraries/reporters/audit-enforcer' + +describe 'Reporter::ChefAutomate methods' do + before :each do + @min_report = { + "profiles": [ + { + "controls": [ + { "id": "c1", "results": [{ "status": 'passed' }] }, + { "id": "c2", "results": [{ "status": 'passed' }] }, + ], + }, + ], + } + @automate = Reporter::AuditEnforcer.new() + end + + it 'is not raising error for a successful InSpec report' do + expect(@automate.send_report(@min_report)).to eq(true) + end + + it 'is not raising error for an InSpec report with no controls' do + expect(@automate.send_report({"profiles": [{"name": "empty"}]})).to eq(true) + end + + it 'is not raising error for an InSpec report with controls but no results' do + expect(@automate.send_report({"profiles": [{ "controls": [{"id": "empty"}]}]})).to eq(true) + end + + it 'raises an error for a failed InSpec report' do + @min_report[:profiles][0][:controls][1][:results][0][:status] = 'failed' + expect{ @automate.send_report(@min_report) }.to raise_error('Audit c2 has failed. Aborting chef-client run.') + end +end From b9c09cabcee50efdca3313a74ec47d05734b02a8 Mon Sep 17 00:00:00 2001 From: Alex Pop Date: Thu, 18 Jul 2019 15:53:37 +0100 Subject: [PATCH 5/5] Fix rubocop lint errors Signed-off-by: Alex Pop --- spec/unit/libraries/audit_enforcer_spec.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/spec/unit/libraries/audit_enforcer_spec.rb b/spec/unit/libraries/audit_enforcer_spec.rb index fd9748bd..ba927eab 100644 --- a/spec/unit/libraries/audit_enforcer_spec.rb +++ b/spec/unit/libraries/audit_enforcer_spec.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 # # Cookbook Name:: audit # Spec:: automate_spec @@ -26,13 +25,13 @@ "profiles": [ { "controls": [ - { "id": "c1", "results": [{ "status": 'passed' }] }, - { "id": "c2", "results": [{ "status": 'passed' }] }, + { "id": 'c1', "results": [{ "status": 'passed' }] }, + { "id": 'c2', "results": [{ "status": 'passed' }] }, ], }, ], } - @automate = Reporter::AuditEnforcer.new() + @automate = Reporter::AuditEnforcer.new end it 'is not raising error for a successful InSpec report' do @@ -40,15 +39,15 @@ end it 'is not raising error for an InSpec report with no controls' do - expect(@automate.send_report({"profiles": [{"name": "empty"}]})).to eq(true) + expect(@automate.send_report({ "profiles": [{ "name": 'empty' }] })).to eq(true) end it 'is not raising error for an InSpec report with controls but no results' do - expect(@automate.send_report({"profiles": [{ "controls": [{"id": "empty"}]}]})).to eq(true) + expect(@automate.send_report({ "profiles": [{ "controls": [{ "id": 'empty' }] }] })).to eq(true) end it 'raises an error for a failed InSpec report' do @min_report[:profiles][0][:controls][1][:results][0][:status] = 'failed' - expect{ @automate.send_report(@min_report) }.to raise_error('Audit c2 has failed. Aborting chef-client run.') + expect { @automate.send_report(@min_report) }.to raise_error('Audit c2 has failed. Aborting chef-client run.') end end