mirror of
https://github.com/inspec/inspec
synced 2024-11-26 06:30:26 +00:00
95c17d4e7f
* Functional test for malformed waiver file Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Raise error for malformed yaml content and exit Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Updates functional test for malformed yaml waiver file and for empty waiver file Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Raises error in case of missing required parameters in waiver file Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Update functional test for missing parameters, extra parameters or column without headers in waiver file Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Fix linting Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Fix warning and error messages Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Updates nil and false check for yaml data and adds additional empty check. Co-authored-by: Sathish Babu <80091550+sathish-progress@users.noreply.github.com> Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Adds more generic message as this yaml reader is now getting used by other functionalities like waiver file Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Fixed test description to reflect correct use case Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Fix validate headers was not validating the required parametes for all the data fields as it was not called inside the loop where we are iterating over the data and fetching the headers. Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Updates the test files for the use case to missing parameters and extra parameters Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Updates code to better handle errors and warnings related to missing required parameters and extra parameters in waivers file in all format i.e (yaml, json and csv). Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Updated functional test to capture the updated error and warning messages for waiver file validation Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Fix linting Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Deleted fixture file which is not required Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> * Refactor: Renamed method validate_headers to reflect whats it's doing and instead of return data in array it will now return the data in hash Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> --------- Signed-off-by: Vasu1105 <vasundhara.jagdale@progress.com> Co-authored-by: Sathish Babu <80091550+sathish-progress@users.noreply.github.com>
330 lines
13 KiB
Ruby
330 lines
13 KiB
Ruby
require "functional/helper"
|
|
|
|
describe "waivers" do
|
|
include FunctionalHelper
|
|
|
|
parallelize_me! # 10s -> 2s w/ N=15
|
|
|
|
let(:waivers_profiles_path) { "#{profile_path}/waivers" }
|
|
let(:run_result) { run_inspec_process(cmd, json: true) }
|
|
let(:controls_by_id) { run_result; @json.dig("profiles", 0, "controls").map { |c| [c["id"], c] }.to_h }
|
|
let(:cmd) { "exec #{waivers_profiles_path}/#{profile_name} --waiver-file #{waivers_profiles_path}/#{profile_name}/files/#{waiver_file}" }
|
|
|
|
attr_accessor :out
|
|
|
|
def inspec(commandline, prefix = nil)
|
|
@stdout = @stderr = nil
|
|
self.out = super
|
|
end
|
|
|
|
def stdout
|
|
@stdout ||= out.stdout
|
|
.force_encoding(Encoding::UTF_8)
|
|
end
|
|
|
|
def stderr
|
|
@stderr ||= out.stderr
|
|
.force_encoding(Encoding::UTF_8)
|
|
end
|
|
|
|
def assert_test_outcome(expected, control_id)
|
|
assert_equal expected, controls_by_id.dig(control_id, "results", 0, "status")
|
|
end
|
|
|
|
def assert_stringy(s)
|
|
assert_kind_of String, s
|
|
refute_empty s
|
|
end
|
|
|
|
def waiver_data(control_id)
|
|
controls_by_id.dig(control_id, "waiver_data")
|
|
end
|
|
|
|
def assert_waiver_annotation(control_id)
|
|
act = waiver_data control_id
|
|
|
|
# extract data from control_id
|
|
expiry = !!(control_id !~ /no_expiry/)
|
|
in_past = !!(control_id =~ /in_past/)
|
|
in_future = !!(control_id =~ /in_future/)
|
|
ran = !!(control_id !~ /not_ran/)
|
|
default_run = !!(control_id =~ /default_run/)
|
|
waiver_expired_in_past = /Waiver expired/ =~ act["message"]
|
|
|
|
# higher logic
|
|
waived = (!expiry && !ran) || (expiry && !ran && in_future)
|
|
# TODO: wasn't message was originally specced as being optional?
|
|
has_message = expiry && !ran && in_past
|
|
|
|
assert_instance_of Hash, act
|
|
|
|
assert_stringy act["justification"] # TODO: optional?
|
|
assert_equal ran, act["run"] unless default_run
|
|
assert_equal waived, act["skipped_due_to_waiver"]
|
|
assert_stringy act["message"] if has_message
|
|
# We supply a message indicating that the waiver has expired in all cases
|
|
assert_equal "", act["message"] unless has_message || waiver_expired_in_past
|
|
end
|
|
|
|
def refute_waiver_annotation(control_id)
|
|
act = waiver_data control_id
|
|
assert_instance_of Hash, act
|
|
assert_empty act
|
|
end
|
|
|
|
def assert_skip_message(yea, nay, control_id = "01_only_if")
|
|
msg = controls_by_id.dig(control_id, "results", 0, "skip_message")
|
|
assert_includes msg, yea
|
|
refute_includes msg, nay
|
|
end
|
|
|
|
describe "a fully pre-slugged control file" do
|
|
let(:profile_name) { "basic" }
|
|
let(:waiver_file) { "waivers.yaml" }
|
|
|
|
# rubocop:disable Layout/AlignHash
|
|
{
|
|
"01_not_waivered_passes" => "passed",
|
|
"02_not_waivered_fails" => "failed",
|
|
"03_waivered_no_expiry_ran_passes" => "passed",
|
|
"04_waivered_no_expiry_ran_fails" => "failed",
|
|
"05_waivered_no_expiry_not_ran" => "skipped",
|
|
"06_waivered_expiry_in_past_ran_passes" => "passed",
|
|
"07_waivered_expiry_in_past_ran_fails" => "failed",
|
|
"08_waivered_expiry_in_past_not_ran" => "passed",
|
|
"09_waivered_expiry_in_future_ran_passes" => "passed",
|
|
"10_waivered_expiry_in_future_ran_fails" => "failed",
|
|
"11_waivered_expiry_in_future_not_ran" => "skipped",
|
|
"12_waivered_expiry_in_future_z_ran_passes" => "passed",
|
|
"13_waivered_expiry_in_future_z_ran_fails" => "failed",
|
|
"14_waivered_expiry_in_future_z_not_ran" => "skipped",
|
|
"15_waivered_expiry_in_future_string_ran_passes" => "passed",
|
|
"16_waivered_expiry_in_future_string_ran_fails" => "failed",
|
|
"17_waivered_expiry_in_future_string_not_ran" => "skipped",
|
|
"18_waivered_no_expiry_default_run" => "failed",
|
|
}.each do |control_id, expected|
|
|
it "has all of the expected outcomes #{control_id}" do
|
|
assert_test_outcome expected, control_id
|
|
|
|
if control_id !~ /not_waivered/
|
|
assert_waiver_annotation control_id
|
|
else
|
|
refute_waiver_annotation control_id
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "a fully pre-slugged control file with csv format waiver file" do
|
|
let(:profile_name) { "basic" }
|
|
let(:waiver_file) { "waivers.csv" }
|
|
|
|
# rubocop:disable Layout/AlignHash
|
|
{
|
|
"01_not_waivered_passes" => "passed",
|
|
"02_not_waivered_fails" => "failed",
|
|
"03_waivered_no_expiry_ran_passes" => "passed",
|
|
"04_waivered_no_expiry_ran_fails" => "failed",
|
|
"05_waivered_no_expiry_not_ran" => "skipped",
|
|
"06_waivered_expiry_in_past_ran_passes" => "passed",
|
|
"14_waivered_expiry_in_future_z_not_ran" => "skipped",
|
|
}.each do |control_id, expected|
|
|
it "has all of the expected outcomes #{control_id}" do
|
|
assert_test_outcome expected, control_id
|
|
|
|
if control_id !~ /not_waivered/
|
|
assert_waiver_annotation control_id
|
|
else
|
|
refute_waiver_annotation control_id
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "a fully pre-slugged control file with json format waiver file" do
|
|
let(:profile_name) { "basic" }
|
|
let(:waiver_file) { "waivers.json" }
|
|
|
|
# rubocop:disable Layout/AlignHash
|
|
{
|
|
"01_not_waivered_passes" => "passed",
|
|
"02_not_waivered_fails" => "failed",
|
|
"03_waivered_no_expiry_ran_passes" => "passed",
|
|
"04_waivered_no_expiry_ran_fails" => "failed",
|
|
"05_waivered_no_expiry_not_ran" => "skipped",
|
|
"06_waivered_expiry_in_past_ran_passes" => "passed",
|
|
"14_waivered_expiry_in_future_z_not_ran" => "skipped",
|
|
}.each do |control_id, expected|
|
|
it "has all of the expected outcomes #{control_id}" do
|
|
assert_test_outcome expected, control_id
|
|
|
|
if control_id !~ /not_waivered/
|
|
assert_waiver_annotation control_id
|
|
else
|
|
refute_waiver_annotation control_id
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "with --filter-waived-controls flag" do
|
|
it "can execute and not hit failures" do
|
|
inspec("exec " + "#{waivers_profiles_path}/purely-broken-controls" + " --filter-waived-controls --waiver-file #{waivers_profiles_path}/purely-broken-controls/files/waivers.yml" + " --no-create-lockfile" + " --no-color")
|
|
_(stderr).must_equal ""
|
|
_(stdout).wont_include("Control Source Code Error")
|
|
_(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 0 controls skipped\n"
|
|
assert_exit_code 0, out
|
|
end
|
|
end
|
|
|
|
describe "with --retain-waiver-data flag" do
|
|
it "can execute and not hit failures with exact same output as normal" do
|
|
inspec("exec " + "#{waivers_profiles_path}/purely-broken-controls" + " --filter-waived-controls --retain-waiver-data --waiver-file #{waivers_profiles_path}/purely-broken-controls/files/waivers.yml" + " --no-create-lockfile" + " --no-color")
|
|
_(stderr).must_equal ""
|
|
_(stdout).wont_include("Control Source Code Error")
|
|
_(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 2 controls skipped\n"
|
|
assert_exit_code 101, out
|
|
end
|
|
end
|
|
|
|
describe "an input and control with the same name" do
|
|
# This is a test for a regression articulated here:
|
|
# https://github.com/inspec/inspec/issues/4936
|
|
it "can execute when control namespace clashes with input" do
|
|
inspec("exec " + "#{waivers_profiles_path}/namespace-clash" + " --no-create-lockfile" + " --no-color")
|
|
|
|
_(stdout).wont_include("Control Source Code Error")
|
|
_(stdout).must_include "\nProfile Summary: 1 successful control, 0 control failures, 0 controls skipped\n"
|
|
_(stderr).must_equal ""
|
|
assert_exit_code 0, out
|
|
end
|
|
end
|
|
|
|
describe "an inherited profile" do
|
|
let(:profile_name) { "waiver-wrapper" }
|
|
let(:waiver_file) { "waivers.yaml" }
|
|
it "should set the data in the child but be empty in the wrapper" do
|
|
run_result
|
|
child_profile = @json["profiles"].detect { |p| p["name"] == "waiver-child" }
|
|
child_waiver_data = child_profile.dig("controls", 0, "waiver_data")
|
|
assert_instance_of Hash, child_waiver_data
|
|
refute_empty child_waiver_data
|
|
expected_child_waiver_data = {
|
|
"run" => false,
|
|
"justification" => "I said so",
|
|
"skipped_due_to_waiver" => true,
|
|
"message" => "",
|
|
}
|
|
assert_equal expected_child_waiver_data, child_waiver_data
|
|
|
|
wrapper_profile = @json["profiles"].detect { |p| p["name"] == "waiver-wrapper" }
|
|
wrapper_waiver_data = wrapper_profile.dig("controls", 0, "waiver_data")
|
|
assert_instance_of Hash, wrapper_waiver_data
|
|
assert_empty wrapper_waiver_data
|
|
end
|
|
end
|
|
|
|
# describe "a profile whose control ids require transformation"
|
|
|
|
describe "a waiver file with invalid dates" do
|
|
let(:profile_name) { "short" }
|
|
let(:waiver_file) { "bad-date.yaml" }
|
|
it "gracefully errors" do
|
|
result = run_result
|
|
assert_includes "ERROR", result.stdout # the error level
|
|
assert_includes "01_small", result.stdout # the offending control ID
|
|
assert_includes "never", result.stdout # The bad value
|
|
assert_equal 1, result.exit_status
|
|
end
|
|
end
|
|
|
|
describe "waivers and only_if" do
|
|
let(:profile_name) { "only_if" }
|
|
|
|
describe "when an only_if is used with empty waiver file" do
|
|
let(:waiver_file) { "empty.yaml" }
|
|
|
|
it "raise warning unable to parse empty.yaml file error" do
|
|
result = run_result
|
|
assert_includes result.stderr, "WARN: Unable to parse"
|
|
assert_includes result.stderr, "YAML file is empty."
|
|
if windows?
|
|
assert_equal 1, result.exit_status
|
|
else
|
|
assert_equal 102, result.exit_status
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "when an only_if is used with waiver file which has waived control with past expiration date" do
|
|
let(:waiver_file) { "waiver.yaml" }
|
|
it "skips the control with a waiver message" do
|
|
assert_skip_message "test_message_from_dsl_02_only_if", "waiver", "02_only_if_when_waiver_is_expired"
|
|
end
|
|
end
|
|
|
|
describe "when both a skipping waiver and an only_if are present" do
|
|
let(:waiver_file) { "waiver.yaml" }
|
|
it "skips the control with a waiver message" do
|
|
assert_skip_message "waiver", "due to only_if"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "with a waiver file that does not exist" do
|
|
let(:profile_name) { "basic" }
|
|
let(:waiver_file) { "no_file.yaml" }
|
|
it "raise file does not exist standard error" do
|
|
result = run_result
|
|
assert_includes result.stderr, "no_file.yaml does not exist"
|
|
assert_equal 1, result.exit_status
|
|
end
|
|
end
|
|
|
|
describe "with a waiver file with wrong headers" do
|
|
let(:profile_name) { "basic" }
|
|
|
|
describe "using csv file" do
|
|
let(:waiver_file) { "wrong-headers.csv" }
|
|
it "raise errors" do
|
|
result = run_result
|
|
assert_includes result.stderr, "ERROR: Error reading waivers file"
|
|
assert_includes result.stderr, "Missing required header/s [\"control_id\", \"justification\"]"
|
|
assert_includes result.stderr, "Fix headers in file to proceed."
|
|
end
|
|
end
|
|
|
|
describe "using json file" do
|
|
let(:waiver_file) { "wrong-headers.json" }
|
|
it "raise errors" do
|
|
result = run_result
|
|
assert_includes result.stderr, "ERROR: Error reading waivers file"
|
|
assert_includes result.stderr, "ERROR: Control ID 04_waivered_no_expiry_ran_fails: missing required parameter/s [\"justification\"]"
|
|
assert_includes result.stderr, "Fix parameters in file to proceed."
|
|
assert_includes result.stderr, "WARN: Control ID 03_waivered_no_expiry_ran_passes: extra parameter/s [\"run_random\", \"expiration_date_random\"]"
|
|
end
|
|
end
|
|
|
|
describe "using yaml file" do
|
|
let(:waiver_file) { "wrong-headers.yaml" }
|
|
it "raise errors" do
|
|
result = run_result
|
|
assert_includes result.stderr, "ERROR: Error reading waivers file"
|
|
assert_includes result.stderr, "ERROR: Control ID 04_waivered_no_expiry_ran_fails: missing required parameter/s [\"justification\"]"
|
|
assert_includes result.stderr, "Fix parameters in file to proceed."
|
|
assert_includes result.stderr, "WARN: Control ID 03_waivered_no_expiry_ran_passes: extra parameter/s [\"run_random\", \"expiration_date_random\"]"
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "with a waiver file with malformed data" do
|
|
let(:profile_name) { "basic" }
|
|
let(:waiver_file) { "malformed-waiver.yaml" }
|
|
|
|
it "raise error" do
|
|
result = run_result
|
|
assert_includes result.stderr, "ERROR:"
|
|
assert_includes result.stderr, "invalid YAML or contents is not a Hash"
|
|
end
|
|
end
|
|
end
|