#!/bin/bash # System Checks Module # Combines configuration, security, updates, and health checks set -euo pipefail # ============================================================================ # Configuration Checks # ============================================================================ check_touchid_sudo() { # Check if Touch ID is configured for sudo local pam_file="/etc/pam.d/sudo" if [[ -f "$pam_file" ]] && grep -q "pam_tid.so" "$pam_file" 2> /dev/null; then echo -e " ${GREEN}✓${NC} Touch ID Enabled for sudo" else # Check if Touch ID is supported local is_supported=false if command -v bioutil > /dev/null 2>&1; then if bioutil -r 2> /dev/null | grep -q "Touch ID"; then is_supported=true fi elif [[ "$(uname -m)" == "arm64" ]]; then is_supported=true fi if [[ "$is_supported" == "true" ]]; then echo -e " ${YELLOW}${ICON_WARNING}${NC} Touch ID ${YELLOW}Not configured${NC} for sudo" export TOUCHID_NOT_CONFIGURED=true fi fi } check_rosetta() { # Check Rosetta 2 (for Apple Silicon Macs) if [[ "$(uname -m)" == "arm64" ]]; then if [[ -f "/Library/Apple/usr/share/rosetta/rosetta" ]]; then echo -e " ${GREEN}✓${NC} Rosetta 2 Installed" else echo -e " ${YELLOW}${ICON_WARNING}${NC} Rosetta 2 ${YELLOW}Not installed${NC}" export ROSETTA_NOT_INSTALLED=true fi fi } check_git_config() { # Check basic Git configuration if command -v git > /dev/null 2>&1; then local git_name=$(git config --global user.name 2> /dev/null || echo "") local git_email=$(git config --global user.email 2> /dev/null || echo "") if [[ -n "$git_name" && -n "$git_email" ]]; then echo -e " ${GREEN}✓${NC} Git Config Configured" else echo -e " ${YELLOW}${ICON_WARNING}${NC} Git Config ${YELLOW}Not configured${NC}" fi fi } check_all_config() { check_touchid_sudo check_rosetta check_git_config } # ============================================================================ # Security Checks # ============================================================================ check_filevault() { # Check FileVault encryption status if command -v fdesetup > /dev/null 2>&1; then local fv_status=$(fdesetup status 2> /dev/null || echo "") if echo "$fv_status" | grep -q "FileVault is On"; then echo -e " ${GREEN}✓${NC} FileVault Enabled" else echo -e " ${RED}✗${NC} FileVault ${RED}Disabled${NC} (Recommend enabling)" export FILEVAULT_DISABLED=true fi fi } check_firewall() { # Check firewall status unset FIREWALL_DISABLED local firewall_status=$(defaults read /Library/Preferences/com.apple.alf globalstate 2> /dev/null || echo "0") if [[ "$firewall_status" == "1" || "$firewall_status" == "2" ]]; then echo -e " ${GREEN}✓${NC} Firewall Enabled" else echo -e " ${YELLOW}${ICON_WARNING}${NC} Firewall ${YELLOW}Disabled${NC} (Consider enabling)" echo -e " ${GRAY}System Settings → Network → Firewall, or run:${NC}" echo -e " ${GRAY}sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 1${NC}" export FIREWALL_DISABLED=true fi } check_gatekeeper() { # Check Gatekeeper status if command -v spctl > /dev/null 2>&1; then local gk_status=$(spctl --status 2> /dev/null || echo "") if echo "$gk_status" | grep -q "enabled"; then echo -e " ${GREEN}✓${NC} Gatekeeper Active" unset GATEKEEPER_DISABLED else echo -e " ${YELLOW}${ICON_WARNING}${NC} Gatekeeper ${YELLOW}Disabled${NC}" echo -e " ${GRAY}Enable via System Settings → Privacy & Security, or:${NC}" echo -e " ${GRAY}sudo spctl --master-enable${NC}" export GATEKEEPER_DISABLED=true fi fi } check_sip() { # Check System Integrity Protection if command -v csrutil > /dev/null 2>&1; then local sip_status=$(csrutil status 2> /dev/null || echo "") if echo "$sip_status" | grep -q "enabled"; then echo -e " ${GREEN}✓${NC} SIP Enabled" else echo -e " ${YELLOW}${ICON_WARNING}${NC} SIP ${YELLOW}Disabled${NC}" echo -e " ${GRAY}Restart into Recovery → Utilities → Terminal → run: csrutil enable${NC}" fi fi } check_all_security() { check_filevault check_firewall check_gatekeeper check_sip } # ============================================================================ # Software Update Checks # ============================================================================ # Cache configuration CACHE_DIR="${HOME}/.cache/mole" CACHE_TTL=600 # 10 minutes in seconds # Ensure cache directory exists mkdir -p "$CACHE_DIR" 2> /dev/null || true clear_cache_file() { local file="$1" rm -f "$file" 2> /dev/null || true } reset_brew_cache() { clear_cache_file "$CACHE_DIR/brew_updates" } reset_softwareupdate_cache() { clear_cache_file "$CACHE_DIR/softwareupdate_list" SOFTWARE_UPDATE_LIST="" } reset_mole_cache() { clear_cache_file "$CACHE_DIR/mole_version" } # Check if cache is still valid is_cache_valid() { local cache_file="$1" local ttl="${2:-$CACHE_TTL}" if [[ ! -f "$cache_file" ]]; then return 1 fi local cache_age=$(($(date +%s) - $(get_file_mtime "$cache_file"))) [[ $cache_age -lt $ttl ]] } check_homebrew_updates() { if ! command -v brew > /dev/null 2>&1; then return fi local cache_file="$CACHE_DIR/brew_updates" local formula_count=0 local cask_count=0 if is_cache_valid "$cache_file"; then read -r formula_count cask_count < "$cache_file" 2> /dev/null || true formula_count=${formula_count:-0} cask_count=${cask_count:-0} else # Show spinner while checking if [[ -t 1 ]]; then start_inline_spinner "Checking Homebrew..." fi local outdated_list="" outdated_list=$(brew outdated --quiet 2> /dev/null || echo "") if [[ -n "$outdated_list" ]]; then formula_count=$(echo "$outdated_list" | wc -l | tr -d ' ') fi local cask_list="" cask_list=$(brew outdated --cask --quiet 2> /dev/null || echo "") if [[ -n "$cask_list" ]]; then cask_count=$(echo "$cask_list" | wc -l | tr -d ' ') fi echo "$formula_count $cask_count" > "$cache_file" 2> /dev/null || true # Stop spinner before output if [[ -t 1 ]]; then stop_inline_spinner fi fi local total_count=$((formula_count + cask_count)) export BREW_FORMULA_OUTDATED_COUNT=$formula_count export BREW_CASK_OUTDATED_COUNT=$cask_count export BREW_OUTDATED_COUNT=$total_count if [[ $total_count -gt 0 ]]; then local breakdown="" if [[ $formula_count -gt 0 && $cask_count -gt 0 ]]; then breakdown=" (${formula_count} formula, ${cask_count} cask)" elif [[ $formula_count -gt 0 ]]; then breakdown=" (${formula_count} formula)" elif [[ $cask_count -gt 0 ]]; then breakdown=" (${cask_count} cask)" fi echo -e " ${YELLOW}${ICON_WARNING}${NC} Homebrew ${YELLOW}${total_count} updates${NC}${breakdown}" echo -e " ${GRAY}Run: ${GREEN}brew upgrade${NC} ${GRAY}and/or${NC} ${GREEN}brew upgrade --cask${NC}" else echo -e " ${GREEN}✓${NC} Homebrew Up to date" fi } # Cache software update list to avoid calling softwareupdate twice SOFTWARE_UPDATE_LIST="" get_software_updates() { local cache_file="$CACHE_DIR/softwareupdate_list" if [[ -z "$SOFTWARE_UPDATE_LIST" ]]; then # Check cache first if is_cache_valid "$cache_file"; then SOFTWARE_UPDATE_LIST=$(cat "$cache_file" 2> /dev/null || echo "") else # Show spinner while checking (only on first call) local show_spinner=false if [[ -t 1 && -z "${SOFTWAREUPDATE_SPINNER_SHOWN:-}" ]]; then start_inline_spinner "Checking system updates..." show_spinner=true export SOFTWAREUPDATE_SPINNER_SHOWN="true" fi SOFTWARE_UPDATE_LIST=$(softwareupdate -l 2> /dev/null || echo "") # Save to cache echo "$SOFTWARE_UPDATE_LIST" > "$cache_file" 2> /dev/null || true # Stop spinner if [[ "$show_spinner" == "true" ]]; then stop_inline_spinner fi fi fi echo "$SOFTWARE_UPDATE_LIST" } check_appstore_updates() { local spinner_started=false if [[ -t 1 ]]; then printf " Checking App Store updates...\r" start_inline_spinner "Checking App Store updates..." spinner_started=true export SOFTWAREUPDATE_SPINNER_SHOWN="external" else echo "Checking App Store updates..." fi local update_list="" update_list=$(get_software_updates | grep -v "Software Update Tool" | grep "^\*" | grep -vi "macOS" || echo "") if [[ "$spinner_started" == "true" ]]; then stop_inline_spinner unset SOFTWAREUPDATE_SPINNER_SHOWN fi local update_count=0 if [[ -n "$update_list" ]]; then update_count=$(echo "$update_list" | wc -l | tr -d ' ') fi export APPSTORE_UPDATE_COUNT=$update_count if [[ $update_count -gt 0 ]]; then echo -e " ${YELLOW}${ICON_WARNING}${NC} App Store ${YELLOW}${update_count} apps${NC} need update" echo -e " ${GRAY}Run: ${GREEN}softwareupdate -i