2018-12-24 14:12:59 +01:00
|
|
|
#!/bin/bash
|
2019-01-24 11:11:08 +01:00
|
|
|
# run-shellcheck
|
2018-12-24 14:12:59 +01:00
|
|
|
# stop on any error
|
|
|
|
set -e
|
|
|
|
# stop on undefined variable
|
|
|
|
set -u
|
|
|
|
|
|
|
|
mytmpdir=$(mktemp -d -t debian-cis-test.XXXXXX)
|
2019-01-23 12:54:33 +01:00
|
|
|
totalerrors=255
|
2018-12-24 14:12:59 +01:00
|
|
|
|
|
|
|
cleanup_and_exit() {
|
|
|
|
rm -rf "$mytmpdir"
|
2019-01-24 15:53:09 +01:00
|
|
|
if [ "$totalerrors" -eq 255 ]; then
|
|
|
|
fatal "RUNTIME ERROR"
|
|
|
|
fi
|
2019-01-23 12:54:33 +01:00
|
|
|
exit $totalerrors
|
2018-12-24 14:12:59 +01:00
|
|
|
}
|
|
|
|
trap "cleanup_and_exit" EXIT HUP INT
|
|
|
|
|
|
|
|
outdir="$mytmpdir/out"
|
|
|
|
mkdir -p "$outdir" || exit 1
|
|
|
|
|
|
|
|
tests_list=""
|
|
|
|
testno=0
|
|
|
|
testcount=0
|
|
|
|
|
2019-02-14 17:52:45 +01:00
|
|
|
skip_tests=0
|
2018-12-24 14:12:59 +01:00
|
|
|
dismiss_count=0
|
|
|
|
nbfailedret=0
|
|
|
|
nbfailedgrep=0
|
|
|
|
nbfailedconsist=0
|
|
|
|
listfailedret=""
|
|
|
|
listfailedgrep=""
|
|
|
|
listfailedconsist=""
|
|
|
|
|
|
|
|
usecase=""
|
|
|
|
usecase_name=""
|
|
|
|
usecase_name_root=""
|
|
|
|
usecase_name_sudo=""
|
|
|
|
declare -a REGISTERED_TESTS
|
|
|
|
|
|
|
|
#####################
|
|
|
|
# Utility functions #
|
|
|
|
#####################
|
|
|
|
# in case a fatal event occurs, fatal logs and exits with return code 1
|
|
|
|
fatal() {
|
2019-01-24 15:53:09 +01:00
|
|
|
printf "\033[1;91m###### \033[0m\n" >&2
|
2018-12-24 14:12:59 +01:00
|
|
|
printf "%b %b\n" "\033[1;91mFATAL\033[0m" "$*" >&2
|
|
|
|
printf "%b \n" "\033[1;91mEXIT TEST SUITE WITH FAILURE\033[0m" >&2
|
2019-01-24 15:53:09 +01:00
|
|
|
trap - EXIT
|
|
|
|
exit 255
|
2018-12-24 14:12:59 +01:00
|
|
|
}
|
|
|
|
# prints that a test failed
|
|
|
|
fail() {
|
|
|
|
printf "%b %b\n" "\033[1;30m\033[41m[FAIL]\033[0m" "$*" >&2
|
|
|
|
}
|
|
|
|
# prints that a test succeded
|
|
|
|
ok() {
|
|
|
|
printf "%b %b\n" "\033[30m\033[42m[ OK ]\033[0m" "$*" >&2
|
|
|
|
}
|
|
|
|
|
|
|
|
# retrieves audit script logfile
|
|
|
|
get_stdout()
|
|
|
|
{
|
|
|
|
cat "$outdir"/"$usecase_name".log
|
|
|
|
}
|
|
|
|
|
|
|
|
# Reset the list of test assertions
|
|
|
|
clear_registered_tests() {
|
|
|
|
unset REGISTERED_TESTS
|
|
|
|
declare -a REGISTERED_TESTS
|
2019-02-14 17:52:45 +01:00
|
|
|
# shellcheck disable=2034
|
2018-12-24 14:12:59 +01:00
|
|
|
dismiss_count=0
|
2019-02-14 17:52:45 +01:00
|
|
|
skip_tests=0
|
2018-12-24 14:12:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
# Generates a formated test name
|
|
|
|
make_usecase_name() {
|
|
|
|
usecase=$1
|
|
|
|
shift
|
|
|
|
role=$1
|
|
|
|
usecase_name=$(printf '%03d-%s-%s-%s' "$testno" "$name" "$usecase" "$role" | sed -re "s=/=_=g")
|
|
|
|
echo -n "$usecase_name"
|
|
|
|
}
|
|
|
|
|
|
|
|
# Plays the registered test suite
|
|
|
|
play_registered_tests() {
|
2019-02-14 17:52:45 +01:00
|
|
|
if [ "$skip_tests" -eq 1 ]; then
|
2019-01-30 11:06:49 +01:00
|
|
|
return
|
|
|
|
fi
|
2018-12-24 14:12:59 +01:00
|
|
|
usecase_name=$1
|
|
|
|
if [[ "${REGISTERED_TESTS[*]}" ]]; then
|
|
|
|
export numtest=${#REGISTERED_TESTS[@]}
|
|
|
|
for t in "${!REGISTERED_TESTS[@]}"; do
|
|
|
|
${REGISTERED_TESTS[$t]}
|
|
|
|
done
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
# Plays comparison tests to ensure that root and sudo exection have the same output
|
|
|
|
play_consistency_tests() {
|
|
|
|
consist_test=0
|
|
|
|
printf "\033[34m*** [%03d] %s::%s Root/Sudo Consistency Tests\033[0m\n" "$testno" "$test_file" "$usecase"
|
|
|
|
retfile_root=$outdir/${usecase_name_root}.retval
|
|
|
|
retfile_sudo=$outdir/${usecase_name_sudo}.retval
|
|
|
|
ret=$(eval cmp "$retfile_root" "$retfile_sudo")
|
|
|
|
if [[ ! 0 -eq $ret ]] ; then
|
|
|
|
fail "$name" return values differ
|
|
|
|
diff "$retfile_root" "$retfile_sudo"
|
|
|
|
consist_test=1
|
|
|
|
else
|
|
|
|
ok "$name return values are equal"
|
|
|
|
|
|
|
|
fi
|
|
|
|
retfile_root=$outdir/${usecase_name_root}.log
|
|
|
|
retfile_sudo=$outdir/${usecase_name_sudo}.log
|
|
|
|
cmp "$retfile_root" "$retfile_sudo" && ret=0 || ret=1
|
|
|
|
if [[ ! 0 -eq $ret ]] ; then
|
|
|
|
fail "$name" logs differ
|
|
|
|
diff "$retfile_root" "$retfile_sudo" || true
|
|
|
|
consist_test=1
|
|
|
|
else
|
|
|
|
ok "$name logs are identical"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ 1 -eq $consist_test ]; then
|
2019-02-14 17:44:13 +01:00
|
|
|
nbfailedconsist=$(( nbfailedconsist + 1 ))
|
|
|
|
listfailedconsist="$listfailedconsist $(make_usecase_name "$usecase" consist)"
|
2018-12-24 14:12:59 +01:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
# Actually runs one signel audit script
|
|
|
|
_run()
|
|
|
|
{
|
|
|
|
usecase_name=$1
|
|
|
|
shift
|
|
|
|
printf "\033[34m*** [%03d] %s \033[0m(%s)\n" "$testno" "$usecase_name" "$*"
|
|
|
|
bash -c "$*" >"$outdir/$usecase_name.log" && true; echo $? > "$outdir/$usecase_name.retval"
|
|
|
|
ret=$(< "$outdir"/"$usecase_name".retval)
|
|
|
|
get_stdout
|
|
|
|
}
|
|
|
|
|
|
|
|
# Load assertion functions for functionnal tests
|
|
|
|
if [ ! -f "$(dirname "$0")"/lib.sh ]; then
|
|
|
|
fatal "Cannot locate lib.sh"
|
|
|
|
fi
|
|
|
|
# shellcheck source=/opt/debian-cis/tests/lib.sh
|
|
|
|
. "$(dirname "$0")"/lib.sh
|
|
|
|
|
|
|
|
###################
|
|
|
|
# Execution start #
|
|
|
|
###################
|
|
|
|
printf "\033[1;36m###\n### %s\n### \033[0m\n" "Starting debian-cis functional testing"
|
|
|
|
|
|
|
|
# if no scripts were passed as arguments, list all available test scenarii to be played
|
|
|
|
if [ $# -eq 0 ]; then
|
|
|
|
tests_list=$(ls -v "$(dirname "$0")"/hardening/)
|
|
|
|
testcount=$(wc -l <<< "$tests_list")
|
|
|
|
else
|
|
|
|
tests_list="$*"
|
|
|
|
testcount=$#
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
for test_file in $tests_list; do
|
|
|
|
test_file_path=$(dirname "$0")/hardening/"$test_file"
|
|
|
|
if [ ! -f "$test_file_path" ]; then
|
|
|
|
fatal "Test file \"$test_file\" does not exist"
|
|
|
|
fi
|
|
|
|
# script var is used inside test files
|
|
|
|
# shellcheck disable=2034
|
|
|
|
script="$(basename "$test_file" .sh)"
|
|
|
|
# source test scenario file to add `test_audit` func
|
|
|
|
# shellcheck disable=1090
|
|
|
|
. "$test_file_path"
|
|
|
|
testno=$(( testno + 1 ))
|
|
|
|
# shellcheck disable=2001
|
|
|
|
name="$(echo "${test_file%%.sh}" | sed 's/\d+\.\d+_//' )"
|
|
|
|
printf "\033[1;36m### [%03d/%03d] %s \033[0m\n" "$testno" "$testcount" "$test_file"
|
|
|
|
# test_audit is the function defined in $test_file, that carries the actual functional tests for this script
|
|
|
|
test_audit
|
|
|
|
# reset var names
|
|
|
|
usecase_name=""
|
|
|
|
usecase_name_root=""
|
|
|
|
usecase_name_sudo=""
|
|
|
|
unset -f test_audit
|
|
|
|
echo ""
|
|
|
|
done
|
|
|
|
|
|
|
|
printf "\033[1;36m###\n### %s \033[0m\n" "Test report"
|
|
|
|
if [ $((nbfailedret + nbfailedgrep + nbfailedconsist )) -eq 0 ] ; then
|
|
|
|
echo -e "\033[42m\033[30mAll tests succeeded :)\033[0m"
|
|
|
|
else
|
|
|
|
(
|
|
|
|
echo -e "\033[41mOne or more tests failed :(\033[0m"
|
|
|
|
echo -e "- $nbfailedret unexpected return values ${listfailedret}"
|
|
|
|
echo -e "- $nbfailedgrep unexpected text values $listfailedgrep"
|
|
|
|
echo -e "- $nbfailedconsist root/sudo consistency $listfailedconsist"
|
|
|
|
) | tee "$outdir"/summary
|
|
|
|
fi
|
|
|
|
echo
|
|
|
|
|
|
|
|
set +e
|
|
|
|
set +u
|
|
|
|
let totalerrors=$((nbfailedret + nbfailedgrep + nbfailedconsist ))
|
|
|
|
# leave `exit 255` for runtime errors
|
|
|
|
[ $totalerrors -ge 255 ] && totalerrors=254
|
|
|
|
exit $totalerrors
|