Cybersecurity-Projects/PROJECTS/beginner/linux-cis-hardening-auditor/src/lib/engine.sh

104 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# ©AngelaMos | 2026
# engine.sh
#
# Score computation engine for CIS audit results
#
# Iterates recorded check results to tally per-section pass/fail/warn/skip
# counts, then computes percentage scores at three levels: per-section
# (SCORE_BY_SECTION), per-CIS-level (SCORE_LEVEL1, SCORE_LEVEL2), and
# overall (SCORE_OVERALL). Scores are calculated as pass/(pass+fail)*100,
# ignoring warn and skip results. Sections with zero scored results
# receive "N/A" instead of a numeric score.
#
# Connects to:
# lib/constants.sh - SECTION_ORDER, STATUS_PASS/FAIL/WARN/SKIP
# lib/registry.sh - RESULT_ORDER, RESULT_STATUS, CTRL_SECTION,
# CTRL_LEVEL, CTRL_SCORED, TOTAL_PASS/FAIL
declare -gA SCORE_BY_SECTION
declare -gA SECTION_PASS
declare -gA SECTION_FAIL
declare -gA SECTION_WARN
declare -gA SECTION_SKIP
declare -g SCORE_OVERALL=0
declare -g SCORE_LEVEL1=0
declare -g SCORE_LEVEL2=0
compute_scores() {
local section
for section in "${SECTION_ORDER[@]}"; do
SECTION_PASS["$section"]=0
SECTION_FAIL["$section"]=0
SECTION_WARN["$section"]=0
SECTION_SKIP["$section"]=0
done
local id
for id in "${RESULT_ORDER[@]}"; do
section="${CTRL_SECTION[$id]}"
case "${RESULT_STATUS[$id]}" in
"$STATUS_PASS") ((SECTION_PASS["$section"]++)) || true ;;
"$STATUS_FAIL") ((SECTION_FAIL["$section"]++)) || true ;;
"$STATUS_WARN") ((SECTION_WARN["$section"]++)) || true ;;
"$STATUS_SKIP") ((SECTION_SKIP["$section"]++)) || true ;;
esac
done
for section in "${SECTION_ORDER[@]}"; do
local p="${SECTION_PASS[$section]}"
local f="${SECTION_FAIL[$section]}"
local total=$((p + f))
if (( total > 0 )); then
SCORE_BY_SECTION["$section"]=$(awk "BEGIN { printf \"%.1f\", ($p / $total) * 100 }")
else
SCORE_BY_SECTION["$section"]="N/A"
fi
done
local scored_total=$((TOTAL_PASS + TOTAL_FAIL))
if (( scored_total > 0 )); then
SCORE_OVERALL=$(awk "BEGIN { printf \"%.1f\", ($TOTAL_PASS / $scored_total) * 100 }")
else
SCORE_OVERALL="0.0"
fi
_compute_level_score 1
SCORE_LEVEL1="$_LEVEL_SCORE"
_compute_level_score 2
SCORE_LEVEL2="$_LEVEL_SCORE"
}
_compute_level_score() {
local level="$1"
local pass=0
local fail=0
local id
for id in "${RESULT_ORDER[@]}"; do
if [[ "${CTRL_LEVEL[$id]}" == "$level" ]]; then
case "${RESULT_STATUS[$id]}" in
"$STATUS_PASS") ((pass++)) || true ;;
"$STATUS_FAIL") ((fail++)) || true ;;
esac
fi
done
local total=$((pass + fail))
if (( total > 0 )); then
_LEVEL_SCORE=$(awk "BEGIN { printf \"%.1f\", ($pass / $total) * 100 }")
else
_LEVEL_SCORE="N/A"
fi
}
get_section_total() {
local section="$1"
local p="${SECTION_PASS[$section]:-0}"
local f="${SECTION_FAIL[$section]:-0}"
local w="${SECTION_WARN[$section]:-0}"
local s="${SECTION_SKIP[$section]:-0}"
echo $((p + f + w + s))
}