Skip to content

Instantly share code, notes, and snippets.

@pwittchen
Last active March 26, 2026 00:56
Show Gist options
  • Select an option

  • Save pwittchen/8d594ec0f570e36788bcd61fefbb8ea9 to your computer and use it in GitHub Desktop.

Select an option

Save pwittchen/8d594ec0f570e36788bcd61fefbb8ea9 to your computer and use it in GitHub Desktop.
bash script for checking litellm vulnerability in the current OS (macOS/Linux)
#!/usr/bin/env bash
#
# check_litellm.sh — Detect current or past installation of compromised
# litellm versions (1.82.7, 1.82.8) from the March 2026 supply chain attack.
#
# Works on macOS and Linux. Checks pip, pip3, poetry, conda, and common
# virtual-environment locations.
#
# Usage: bash check_litellm.sh
#
# Reference: https://docs.litellm.ai/blog/security-update-march-2026
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BOLD='\033[1m'
RESET='\033[0m'
FOUND_ISSUES=0
WARN_COUNT=0
OK_COUNT=0
INFO_COUNT=0
WARNINGS=()
WARN_DETAILS=()
OK_MSGS=()
INFO_MSGS=()
OS="$(uname -s)"
# Tracking variables for summary detail sections
UNIFIED_LOG_STATUS="" # "clean", "found", or "skipped"
UNIFIED_LOG_HITS=""
VARLOG_STATUS="" # "clean", "found", or ""
VARLOG_HITS=""
JOURNAL_STATUS="" # "clean", "found", or ""
JOURNAL_HITS=""
PIP_LOG_STATUS="" # "clean", "found_malicious", "found_nonmalicious", or "clean"
PIP_LOG_DETAILS=""
TOTAL_STEPS=9
CURRENT_STEP=0
TERM_COLS="${COLUMNS:-$(tput cols 2>/dev/null || echo 80)}"
LOG_FILE="/tmp/check_litellm_$(date +%Y%m%d_%H%M%S).log"
progress() {
local msg=" $1"
# Truncate to terminal width to avoid wrapping
if (( ${#msg} > TERM_COLS - 1 )); then
msg="${msg:0:$((TERM_COLS - 4))}..."
fi
# Write directly to terminal so progress lines stay out of the log file
printf '\r%-*s\r' "$TERM_COLS" "" >/dev/tty 2>/dev/null || true
printf '\r%s' "$msg" >/dev/tty 2>/dev/null || true
}
clear_progress() {
printf '\r%-*s\r' "$TERM_COLS" "" >/dev/tty 2>/dev/null || true
}
warn() { FOUND_ISSUES=1; WARN_COUNT=$((WARN_COUNT + 1)); WARNINGS+=("$*"); WARN_DETAILS+=(""); }
# Append detail lines to the most recent warning (for the summary)
warn_detail() {
local idx=$(( ${#WARN_DETAILS[@]} - 1 ))
if [[ -n "${WARN_DETAILS[$idx]}" ]]; then
WARN_DETAILS[$idx]="${WARN_DETAILS[$idx]}"$'\n'"$1"
else
WARN_DETAILS[$idx]="$1"
fi
}
ok() { OK_COUNT=$((OK_COUNT + 1)); OK_MSGS+=("$*"); }
info() { INFO_COUNT=$((INFO_COUNT + 1)); INFO_MSGS+=("$*"); }
header(){ CURRENT_STEP=$((CURRENT_STEP + 1)); progress "[${CURRENT_STEP}/${TOTAL_STEPS}] $*"; }
MALICIOUS_VERSIONS="1.82.7|1.82.8"
C2_DOMAIN="models.litellm.cloud"
PTH_FILE="litellm_init.pth"
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
check_pip_variant() {
local pip_cmd="$1"
if ! command -v "$pip_cmd" &>/dev/null; then
info "$pip_cmd not found, skipping."
return
fi
local pip_show
pip_show=$("$pip_cmd" show litellm 2>/dev/null) || true
local version
version=$(echo "$pip_show" | grep -i '^Version:' | awk '{print $2}') || true
local location
location=$(echo "$pip_show" | grep -i '^Location:' | awk '{print $2}') || true
if [[ -z "$version" ]]; then
ok "litellm is not installed via $pip_cmd."
elif echo "$version" | grep -qE "^($MALICIOUS_VERSIONS)$"; then
warn "MALICIOUS litellm $version is installed via $pip_cmd!${location:+ (${location})}"
else
info "litellm $version is installed via $pip_cmd (not a known malicious version)."
fi
local cache
cache=$("$pip_cmd" cache list litellm 2>/dev/null) || true
if [[ -n "$cache" ]]; then
if echo "$cache" | grep -qE "litellm.*(${MALICIOUS_VERSIONS})"; then
local bad_cache
bad_cache=$(echo "$cache" | grep -E "(${MALICIOUS_VERSIONS})")
warn "Malicious litellm version found in $pip_cmd cache!"
warn_detail "$bad_cache"
else
info "litellm found in $pip_cmd cache (non-malicious version)."
fi
else
ok "No litellm in $pip_cmd cache."
fi
}
check_poetry() {
if ! command -v poetry &>/dev/null; then
info "poetry not found, skipping."
return
fi
# Check the global poetry cache
local cache_dir
cache_dir=$(poetry config cache-dir 2>/dev/null) || true
if [[ -n "$cache_dir" && -d "$cache_dir" ]]; then
local hits
hits=$(find "$cache_dir" -type f -name "*.whl" -o -name "*.tar.gz" 2>/dev/null \
| grep -i "litellm" || true)
if [[ -n "$hits" ]]; then
if echo "$hits" | grep -qE "(${MALICIOUS_VERSIONS})"; then
local bad_hits
bad_hits=$(echo "$hits" | grep -E "(${MALICIOUS_VERSIONS})")
warn "Malicious litellm artifact found in poetry cache!"
warn_detail "$bad_hits"
else
info "litellm found in poetry cache (non-malicious version)."
fi
else
ok "No litellm in poetry cache."
fi
fi
# Scan poetry.lock files under $HOME for litellm
info "Scanning for poetry.lock files referencing litellm under \$HOME (this may take a moment)..."
local locks
locks=$(find "$HOME" -maxdepth 6 -name "poetry.lock" -type f 2>/dev/null || true)
if [[ -n "$locks" ]]; then
local lock_arr=()
while IFS= read -r l; do lock_arr+=("$l"); done <<< "$locks"
local lock_total=${#lock_arr[@]}
local lock_i=0
for lockfile in "${lock_arr[@]}"; do
lock_i=$((lock_i + 1))
progress "Scanning poetry.lock ($lock_i/$lock_total)... $lockfile"
if grep -qi "litellm" "$lockfile" 2>/dev/null; then
local lock_ver
lock_ver=$(grep -A2 'name = "litellm"' "$lockfile" 2>/dev/null \
| grep 'version' | head -1 | sed 's/.*"\(.*\)".*/\1/' || true)
if echo "$lock_ver" | grep -qE "^($MALICIOUS_VERSIONS)$"; then
warn "poetry.lock pins MALICIOUS litellm $lock_ver: $lockfile"
else
info "poetry.lock references litellm $lock_ver: $lockfile"
fi
fi
done
clear_progress
else
ok "No poetry.lock files found."
fi
}
check_conda() {
if ! command -v conda &>/dev/null; then
info "conda not found, skipping."
return
fi
local envs
envs=$(conda env list 2>/dev/null | grep -v '^#' | awk '{print $1}' | grep -v '^$' || true)
if [[ -z "$envs" ]]; then
ok "No conda environments found."
return
fi
local env_arr=()
while IFS= read -r e; do env_arr+=("$e"); done <<< "$envs"
local env_total=${#env_arr[@]}
local env_i=0
for env in "${env_arr[@]}"; do
env_i=$((env_i + 1))
progress "Scanning conda env ($env_i/$env_total)... $env"
local hit
hit=$(conda list -n "$env" 2>/dev/null | grep -i litellm || true)
if [[ -n "$hit" ]]; then
if echo "$hit" | grep -qE "(${MALICIOUS_VERSIONS})"; then
warn "Malicious litellm in conda env '$env': $hit"
else
info "litellm in conda env '$env' (non-malicious): $hit"
fi
fi
done
clear_progress
ok "Finished scanning conda environments."
}
check_venvs() {
info "Searching for virtual environments under \$HOME (this may take a moment)..."
local site_dirs
site_dirs=$(find "$HOME" -maxdepth 6 \
\( -path "*/lib/python*/site-packages" -o -path "*/lib/site-packages" \) \
-type d 2>/dev/null || true)
if [[ -z "$site_dirs" ]]; then
ok "No Python site-packages directories found."
return
fi
local sp_arr=()
while IFS= read -r s; do sp_arr+=("$s"); done <<< "$site_dirs"
local sp_total=${#sp_arr[@]}
local sp_i=0
local found_any=0
for sp in "${sp_arr[@]}"; do
sp_i=$((sp_i + 1))
progress "Scanning site-packages ($sp_i/$sp_total)... $sp"
# Check for litellm dist-info
local dist
dist=$(find "$sp" -maxdepth 1 -type d -name "litellm-*" 2>/dev/null || true)
if [[ -n "$dist" ]]; then
if echo "$dist" | grep -qE "litellm-(${MALICIOUS_VERSIONS})"; then
warn "Malicious litellm dist-info found: $dist"
else
info "litellm dist-info found (non-malicious): $dist"
fi
found_any=1
fi
# Check for the malicious .pth file
if [[ -f "$sp/$PTH_FILE" ]]; then
warn "MALICIOUS $PTH_FILE found: $sp/$PTH_FILE"
fi
done
clear_progress
if [[ "$found_any" -eq 0 ]]; then
ok "No litellm installations found in virtual environments."
fi
}
check_pth_file() {
info "Searching entire filesystem for $PTH_FILE (this may take a while)..."
local hits
if [[ "$OS" == "Darwin" ]]; then
# Use Spotlight first for speed, fall back to find
progress "Searching filesystem (mdfind)... $PTH_FILE"
hits=$(mdfind -name "$PTH_FILE" 2>/dev/null || true)
if [[ -z "$hits" ]]; then
progress "Searching filesystem (find /)... $PTH_FILE"
hits=$(find / -name "$PTH_FILE" 2>/dev/null || true)
fi
else
progress "Searching filesystem (find /)... $PTH_FILE"
hits=$(find / -name "$PTH_FILE" 2>/dev/null || true)
fi
clear_progress
if [[ -n "$hits" ]]; then
warn "MALICIOUS .pth file found on disk!"
warn_detail "$hits"
else
ok "No $PTH_FILE found."
fi
}
check_persistence() {
if [[ "$OS" == "Linux" ]]; then
# systemd user services
local user_systemd="$HOME/.config/systemd/user"
progress "Checking dir... $user_systemd"
if [[ -d "$user_systemd" ]]; then
local suspicious
suspicious=$(ls -1 "$user_systemd" 2>/dev/null || true)
if [[ -n "$suspicious" ]]; then
info "systemd user services found in $user_systemd — review manually: $suspicious"
fi
else
ok "No systemd user service directory."
fi
# Also check system-level
for dir in /etc/systemd/system /usr/lib/systemd/system; do
progress "Checking dir... $dir"
if [[ -d "$dir" ]]; then
local hits
hits=$(ls -1 "$dir" 2>/dev/null | grep -i litellm || true)
if [[ -n "$hits" ]]; then
warn "Suspicious systemd service referencing litellm in $dir!"
warn_detail "$hits"
fi
fi
done
clear_progress
fi
if [[ "$OS" == "Darwin" ]]; then
# Check LaunchAgents / LaunchDaemons (macOS persistence equivalent)
for dir in "$HOME/Library/LaunchAgents" /Library/LaunchAgents /Library/LaunchDaemons; do
progress "Checking dir... $dir"
if [[ -d "$dir" ]]; then
local hits
hits=$(grep -rl "litellm\|$C2_DOMAIN" "$dir" 2>/dev/null || true)
if [[ -n "$hits" ]]; then
warn "Suspicious LaunchAgent/Daemon referencing litellm or C2 domain!"
warn_detail "$hits"
fi
fi
done
clear_progress
ok "No litellm-related LaunchAgents/Daemons found."
fi
}
check_network_logs() {
info "Checking system logs for C2 domain ($C2_DOMAIN)..."
if [[ "$OS" == "Linux" ]]; then
progress "Scanning /var/log/..."
local hits
hits=$(grep -r "$C2_DOMAIN" /var/log/ 2>/dev/null || true)
if [[ -n "$hits" ]]; then
local trimmed
trimmed=$(echo "$hits" | head -20)
warn "C2 domain found in /var/log/!"
warn_detail "$trimmed"
VARLOG_STATUS="found"
VARLOG_HITS="$trimmed"
else
ok "No references to C2 domain in /var/log/."
VARLOG_STATUS="clean"
fi
# Check journalctl if available
if command -v journalctl &>/dev/null; then
progress "Querying journalctl..."
local jhits
jhits=$(journalctl --no-pager -q 2>/dev/null | grep "$C2_DOMAIN" | head -5 || true)
if [[ -n "$jhits" ]]; then
warn "C2 domain found in journalctl!"
warn_detail "$jhits"
JOURNAL_STATUS="found"
JOURNAL_HITS="$jhits"
else
ok "No references to C2 domain in journalctl."
JOURNAL_STATUS="clean"
fi
fi
clear_progress
fi
if [[ "$OS" == "Darwin" ]]; then
progress "Scanning /var/log/..."
local hits
hits=$(grep -r "$C2_DOMAIN" /var/log/ 2>/dev/null || true)
if [[ -n "$hits" ]]; then
local trimmed
trimmed=$(echo "$hits" | head -20)
warn "C2 domain found in /var/log/!"
warn_detail "$trimmed"
VARLOG_STATUS="found"
VARLOG_HITS="$trimmed"
else
ok "No references to C2 domain in /var/log/."
VARLOG_STATUS="clean"
fi
# Check unified log (last 48h)
if command -v log &>/dev/null; then
progress "Querying unified log (last 48h)..."
info "Checking macOS unified log (last 48h) — this may be slow..."
local uhits
uhits=$(log show --last 48h --predicate "eventMessage CONTAINS '$C2_DOMAIN'" 2>/dev/null \
| head -10 || true)
if [[ -n "$uhits" && ! "$uhits" =~ "No matches" ]]; then
warn "C2 domain found in macOS unified log!"
warn_detail "$uhits"
UNIFIED_LOG_STATUS="found"
UNIFIED_LOG_HITS="$uhits"
else
ok "No references to C2 domain in macOS unified log (last 48h)."
UNIFIED_LOG_STATUS="clean"
fi
else
UNIFIED_LOG_STATUS="skipped"
fi
clear_progress
fi
}
check_pip_logs() {
local log_dirs=()
if [[ "$OS" == "Darwin" ]]; then
log_dirs+=("$HOME/Library/Logs/pip")
fi
log_dirs+=("$HOME/.pip" "$HOME/.cache/pip/log")
local log_total=${#log_dirs[@]}
local log_i=0
local found=0
for dir in "${log_dirs[@]}"; do
log_i=$((log_i + 1))
progress "Scanning pip logs ($log_i/$log_total)... $dir"
if [[ -d "$dir" ]]; then
local hits
hits=$(grep -r "litellm" "$dir" 2>/dev/null || true)
if [[ -n "$hits" ]]; then
if echo "$hits" | grep -qE "(${MALICIOUS_VERSIONS})"; then
local bad_lines
bad_lines=$(echo "$hits" | grep -E "(${MALICIOUS_VERSIONS})" | head -5)
warn "Malicious litellm version referenced in pip logs: $dir"
warn_detail "$bad_lines"
PIP_LOG_STATUS="found_malicious"
PIP_LOG_DETAILS="${PIP_LOG_DETAILS:+${PIP_LOG_DETAILS}
}$bad_lines"
else
local sample_lines
sample_lines=$(echo "$hits" | head -5)
info "litellm referenced in pip logs (non-malicious version): $dir"
if [[ "$PIP_LOG_STATUS" != "found_malicious" ]]; then
PIP_LOG_STATUS="found_nonmalicious"
fi
PIP_LOG_DETAILS="${PIP_LOG_DETAILS:+${PIP_LOG_DETAILS}
}[non-malicious] $dir:
$sample_lines"
fi
found=1
fi
fi
done
clear_progress
if [[ "$found" -eq 0 ]]; then
ok "No litellm references in pip logs."
PIP_LOG_STATUS="clean"
fi
}
check_shell_history() {
local history_files=(
"$HOME/.bash_history"
"$HOME/.zsh_history"
"$HOME/.local/share/fish/fish_history"
)
local hist_total=${#history_files[@]}
local hist_i=0
local found=0
for hf in "${history_files[@]}"; do
hist_i=$((hist_i + 1))
progress "Checking history file ($hist_i/$hist_total)... $hf"
if [[ -f "$hf" ]]; then
local hits
hits=$(grep -i "litellm" "$hf" 2>/dev/null || true)
if [[ -n "$hits" ]]; then
if echo "$hits" | grep -qE "(${MALICIOUS_VERSIONS})"; then
local bad_lines
bad_lines=$(echo "$hits" | grep -E "(${MALICIOUS_VERSIONS})" | head -10)
warn "Malicious litellm version referenced in $hf!"
warn_detail "$bad_lines"
else
info "litellm referenced in $hf (no malicious version string found)."
fi
found=1
fi
fi
done
clear_progress
if [[ "$found" -eq 0 ]]; then
ok "No litellm references in shell history files."
fi
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
echo -e "${BOLD}=============================================${RESET}"
echo -e "${BOLD} litellm Supply Chain Compromise Checker${RESET}"
echo -e "${BOLD} Malicious versions: 1.82.7, 1.82.8${RESET}"
echo -e "${BOLD} OS detected: $OS${RESET}"
echo -e "${BOLD}=============================================${RESET}"
header "Checking pip / pip3"
check_pip_variant pip
check_pip_variant pip3
header "Checking poetry"
check_poetry
header "Checking conda"
check_conda
header "Checking virtual environments"
check_venvs
header "Searching for malicious .pth file"
check_pth_file
header "Checking persistence mechanisms"
check_persistence
header "Checking network / system logs"
check_network_logs
header "Checking pip install logs"
check_pip_logs
header "Checking shell history"
check_shell_history
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
clear_progress
echo ""
echo -e "${BOLD}=============================================${RESET}"
echo -e "${BOLD} Summary${RESET}"
echo -e "${BOLD}=============================================${RESET}"
echo ""
echo -e " Checks passed: ${GREEN}${OK_COUNT}${RESET}"
echo -e " Warnings: ${RED}${WARN_COUNT}${RESET}"
echo -e " Informational: ${YELLOW}${INFO_COUNT}${RESET}"
# --- System logs — C2 domain ---
echo ""
echo -e "${BOLD} System Logs — C2 domain ($C2_DOMAIN):${RESET}"
case "$VARLOG_STATUS" in
found)
echo -e " ${RED}[!] C2 domain detected in /var/log/${RESET}"
while IFS= read -r line; do
echo " $line"
done <<< "$VARLOG_HITS"
;;
clean)
echo -e " ${GREEN}[OK]${RESET} No references to C2 domain in /var/log/."
;;
esac
case "$JOURNAL_STATUS" in
found)
echo -e " ${RED}[!] C2 domain detected in journalctl${RESET}"
while IFS= read -r line; do
echo " $line"
done <<< "$JOURNAL_HITS"
;;
clean)
echo -e " ${GREEN}[OK]${RESET} No references to C2 domain in journalctl."
;;
esac
case "$UNIFIED_LOG_STATUS" in
found)
echo -e " ${RED}[!] C2 domain detected in macOS unified log (last 48h)${RESET}"
while IFS= read -r line; do
echo " $line"
done <<< "$UNIFIED_LOG_HITS"
;;
clean)
echo -e " ${GREEN}[OK]${RESET} No references to C2 domain in macOS unified log (last 48h)."
;;
skipped)
echo -e " ${YELLOW}[*]${RESET} macOS unified log check was skipped (log command not available)."
;;
esac
# --- pip install logs ---
echo ""
echo -e "${BOLD} pip Install Logs — litellm references:${RESET}"
case "$PIP_LOG_STATUS" in
found_malicious)
echo -e " ${RED}[!] Malicious litellm version found in pip install logs${RESET}"
while IFS= read -r line; do
echo " $line"
done <<< "$PIP_LOG_DETAILS"
;;
found_nonmalicious)
echo -e " ${YELLOW}[*]${RESET} litellm referenced in pip logs (non-malicious version)."
while IFS= read -r line; do
echo " $line"
done <<< "$PIP_LOG_DETAILS"
;;
clean)
echo -e " ${GREEN}[OK]${RESET} No litellm references in pip install logs."
;;
"")
echo -e " ${YELLOW}[*]${RESET} pip log check did not run."
;;
esac
if [[ "$WARN_COUNT" -gt 0 ]]; then
echo ""
echo -e "${RED}${BOLD} Warnings:${RESET}"
for i in "${!WARNINGS[@]}"; do
echo -e " ${RED}[!]${RESET} ${WARNINGS[$i]}"
if [[ -n "${WARN_DETAILS[$i]}" ]]; then
while IFS= read -r detail_line; do
echo " $detail_line"
done <<< "${WARN_DETAILS[$i]}"
fi
done
echo ""
echo "Recommended actions:"
echo " 1. Remove the malicious litellm package immediately."
echo " 2. Rotate ALL credentials on this machine (SSH keys, API tokens,"
echo " cloud credentials, database passwords, .env files)."
echo " 3. Treat the system as compromised — investigate for additional"
echo " payloads and consider reimaging."
echo ""
echo "Reference: https://docs.litellm.ai/blog/security-update-march-2026"
else
echo ""
echo -e "${GREEN}${BOLD} No signs of compromised litellm found.${RESET}"
fi
if [[ "${#INFO_MSGS[@]}" -gt 0 ]]; then
echo ""
echo -e "${YELLOW}${BOLD} Informational:${RESET}"
for msg in "${INFO_MSGS[@]}"; do
echo -e " ${YELLOW}[*]${RESET} $msg"
done
fi
if [[ "${#OK_MSGS[@]}" -gt 0 ]]; then
echo ""
echo -e "${GREEN}${BOLD} Passed:${RESET}"
for msg in "${OK_MSGS[@]}"; do
echo -e " ${GREEN}[OK]${RESET} $msg"
done
fi
echo ""
echo -e "${BOLD}=============================================${RESET}"
# Write plain-text log file
{
echo "============================================="
echo " litellm Supply Chain Compromise Checker"
echo " Malicious versions: 1.82.7, 1.82.8"
echo " OS detected: $OS"
echo "============================================="
echo ""
echo " Checks passed: $OK_COUNT"
echo " Warnings: $WARN_COUNT"
echo " Informational: $INFO_COUNT"
echo ""
echo " System Logs — C2 domain ($C2_DOMAIN):"
case "$VARLOG_STATUS" in
found)
echo " [!] C2 domain detected in /var/log/"
while IFS= read -r line; do
echo " $line"
done <<< "$VARLOG_HITS"
;;
clean)
echo " [OK] No references to C2 domain in /var/log/."
;;
esac
case "$JOURNAL_STATUS" in
found)
echo " [!] C2 domain detected in journalctl"
while IFS= read -r line; do
echo " $line"
done <<< "$JOURNAL_HITS"
;;
clean)
echo " [OK] No references to C2 domain in journalctl."
;;
esac
case "$UNIFIED_LOG_STATUS" in
found)
echo " [!] C2 domain detected in macOS unified log (last 48h)"
while IFS= read -r line; do
echo " $line"
done <<< "$UNIFIED_LOG_HITS"
;;
clean)
echo " [OK] No references to C2 domain in macOS unified log (last 48h)."
;;
skipped)
echo " [*] macOS unified log check was skipped (log command not available)."
;;
esac
echo ""
echo " pip Install Logs — litellm references:"
case "$PIP_LOG_STATUS" in
found_malicious)
echo " [!] Malicious litellm version found in pip install logs"
while IFS= read -r line; do
echo " $line"
done <<< "$PIP_LOG_DETAILS"
;;
found_nonmalicious)
echo " [*] litellm referenced in pip logs (non-malicious version)."
while IFS= read -r line; do
echo " $line"
done <<< "$PIP_LOG_DETAILS"
;;
clean)
echo " [OK] No litellm references in pip install logs."
;;
"")
echo " [*] pip log check did not run."
;;
esac
if [[ "$WARN_COUNT" -gt 0 ]]; then
echo ""
echo " Warnings:"
for i in "${!WARNINGS[@]}"; do
echo " [!] ${WARNINGS[$i]}"
if [[ -n "${WARN_DETAILS[$i]}" ]]; then
while IFS= read -r detail_line; do
echo " $detail_line"
done <<< "${WARN_DETAILS[$i]}"
fi
done
fi
if [[ "${#INFO_MSGS[@]}" -gt 0 ]]; then
echo ""
echo " Informational:"
for msg in "${INFO_MSGS[@]}"; do
echo " [*] $msg"
done
fi
if [[ "${#OK_MSGS[@]}" -gt 0 ]]; then
echo ""
echo " Passed:"
for msg in "${OK_MSGS[@]}"; do
echo " [OK] $msg"
done
fi
echo ""
echo "============================================="
} > "$LOG_FILE"
echo ""
echo "Full log saved to: $LOG_FILE"
exit $FOUND_ISSUES
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment