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

129 lines
3.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# ©AngelaMos | 2026
# utils.sh
#
# Shared utility functions for logging and system inspection
#
# Provides ANSI-colored log helpers (info, success, warn, fail) that
# respect the QUIET flag, inline progress display with carriage-return
# clearing, bash version enforcement, root privilege detection, and
# OS identification from /etc/os-release. Wraps all filesystem access
# through SYSROOT so checks work against both the live system and
# test fixture directories: file_exists, read_file, get_sysctl (proc
# tree then sysctl fallback), get_config_value (grep key from config),
# run_cmd (blocks in test mode), and dpkg/systemctl wrappers.
#
# Connects to:
# lib/constants.sh - ANSI color codes, EXIT_FAIL, MIN_BASH_VERSION
# checks/*.sh - check functions call file_exists, get_sysctl,
# package_is_installed, service_is_enabled, etc.
info() { [[ "$QUIET" == "true" ]] || echo -e "${CYAN}[*]${RESET} $1" >&2; }
success() { [[ "$QUIET" == "true" ]] || echo -e "${GREEN}[✔]${RESET} $1" >&2; }
warn() { [[ "$QUIET" == "true" ]] || echo -e "${YELLOW}[!]${RESET} $1" >&2; }
fail() { echo -e "${RED}[✖]${RESET} $1" >&2; exit "$EXIT_FAIL"; }
progress() {
[[ "$QUIET" == "true" ]] && return
printf "\r${DIM}[%s] Checking: %s${RESET}%s" "$1" "$2" "$(printf '%40s' '')" >&2
}
clear_progress() {
[[ "$QUIET" == "true" ]] && return
printf "\r%80s\r" "" >&2
}
check_bash_version() {
if (( BASH_VERSINFO[0] < MIN_BASH_VERSION )); then
fail "Bash ${MIN_BASH_VERSION}+ required (found ${BASH_VERSION})"
fi
}
check_root() {
if [[ $EUID -ne 0 && "$SYSROOT" == "/" ]]; then
warn "Running without root privileges — some checks will be skipped"
return 1
fi
return 0
}
detect_os() {
local os_release="${SYSROOT}/etc/os-release"
if [[ -f "$os_release" ]]; then
DETECTED_ID=$(grep -oP '^ID=\K.*' "$os_release" | tr -d '"')
DETECTED_VERSION=$(grep -oP '^VERSION_ID=\K.*' "$os_release" | tr -d '"')
else
DETECTED_ID="unknown"
DETECTED_VERSION="unknown"
fi
}
run_cmd() {
if [[ "$SYSROOT" != "/" ]]; then
return 1
fi
"$@" 2>/dev/null
}
file_exists() {
[[ -f "${SYSROOT}${1}" ]]
}
read_file() {
local path="${SYSROOT}${1}"
if [[ -f "$path" ]]; then
cat "$path"
else
return 1
fi
}
get_sysctl() {
local param="$1"
local proc_path="${SYSROOT}/proc/sys/${param//\.//}"
if [[ -f "$proc_path" ]]; then
cat "$proc_path"
return 0
fi
if run_cmd sysctl -n "$param"; then
return 0
fi
return 1
}
get_config_value() {
local file="$1"
local key="$2"
local path="${SYSROOT}${file}"
if [[ ! -f "$path" ]]; then
return 1
fi
grep -Ei "^\s*${key}\s" "$path" | tail -1 | awk '{print $2}'
}
service_is_enabled() {
if run_cmd systemctl is-enabled "$1"; then
return 0
fi
return 1
}
service_is_active() {
if run_cmd systemctl is-active "$1"; then
return 0
fi
return 1
}
package_is_installed() {
# shellcheck disable=SC2016 # ${Status} is dpkg-query template syntax, not shell expansion
if run_cmd dpkg-query -W -f='${Status}' "$1" | grep -q "install ok installed"; then
return 0
fi
return 1
}