#!/bin/bash
# =============================================================================
# security-audit.sh -- Security Audit Tool
# =============================================================================
# Category: security | Bird: falcon | Tier: 2 | Node-aware: yes
#
# Actions:
#   --status          JSON pill
#   --send            Full local security audit
#   --fleet           Fleet-wide aggregated security audit (via fleet_exec)
#   --accounts        Stale/orphaned account scan
#   --keys            Orphaned SSH key scan
#   --sudoers         Sudoers drift check
#   --suid            SUID/SGID binary audit
#   --permissions     Permission baseline check
#   --log             Audit log viewer
#   --verify          Full verification pass
#   --digest          Full report
#   --flush           Rotate audit log
# =============================================================================

TOOL_NAME="security-audit"
TOOL_VERSION="3.0.0"
TOOL_TIER=2
TOOL_BIRD="falcon"
TOOL_LOG="/opt/binary/logs/tools/${TOOL_NAME}.log"

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "${SCRIPT_DIR}/common.sh" ]]; then source "${SCRIPT_DIR}/common.sh"
elif [[ -f "${SCRIPT_DIR}/../common.sh" ]]; then source "${SCRIPT_DIR}/../common.sh"
else echo "FATAL: common.sh not found" >&2; exit 1; fi
export BIRD_ID="${TOOL_BIRD}"

AUDIT_LOG="${AUDIT_LOG:-/opt/binary/logs/audit/audit.log}"
KNOWN_USERS=(root snowdaddy automation phoenix nginx postfix dovecot pdns)
STALE_DAYS=90

# --accounts (stale/orphaned accounts)
_do_accounts_impl() {
    echo "=== Account Audit: ${NODE_ID} ==="
    local stale=0 orphaned=0
    echo "--- Users with shell ---"
    while IFS=: read -r user _ uid _ _ home shell; do
        [[ "$shell" == */nologin ]] || [[ "$shell" == */false ]] && continue
        echo "  ${user} (uid=${uid}, home=${home})"
        # Check last login
        local last; last=$(lastlog -u "$user" 2>/dev/null | tail -1 | awk '{print $4, $5, $9}')
        if echo "$last" | grep -q "Never"; then
            echo "    [WARN] Never logged in"
            stale=$((stale+1))
        fi
        # Check if home exists
        if [ ! -d "$home" ] && [ "$user" != "root" ]; then
            echo "    [WARN] Home directory missing"
            orphaned=$((orphaned+1))
        fi
    done < /etc/passwd 2>/dev/null
    echo ""
    echo "  Stale: ${stale}  Orphaned: ${orphaned}"
    tool_audit "accounts" "stale=${stale} orphaned=${orphaned} node=${NODE_ID}"
}

# --keys (orphaned SSH keys)
_do_keys_impl() {
    echo "=== SSH Key Audit: ${NODE_ID} ==="
    local total=0 orphaned=0
    for user_home in /root /home/*; do
        local ak="${user_home}/.ssh/authorized_keys"
        [ -f "$ak" ] || continue
        local user; user=$(basename "$user_home")
        [ "$user_home" = "/root" ] && user="root"
        local count; count=$(grep -c '^ssh-' "$ak" 2>/dev/null || echo 0)
        total=$((total + count))
        echo "  ${user}: ${count} key(s)"
        # Check for keys with comments matching unknown users
        grep '^ssh-' "$ak" 2>/dev/null | while read -r line; do
            local comment; comment=$(echo "$line" | awk '{print $NF}')
            echo "    ${comment}"
        done
    done
    echo ""
    echo "  Total keys: ${total}"
    tool_audit "keys" "total=${total} node=${NODE_ID}"
}

# --sudoers (drift check)
_do_sudoers_impl() {
    echo "=== Sudoers Audit: ${NODE_ID} ==="
    echo "--- /etc/sudoers.d/ ---"
    ls -la /etc/sudoers.d/ 2>/dev/null | grep -v '^total' | sed 's/^/  /'
    echo ""
    echo "--- NOPASSWD rules ---"
    grep -r 'NOPASSWD' /etc/sudoers /etc/sudoers.d/ 2>/dev/null | sed 's/^/  /'
    echo ""
    echo "--- ALL=(ALL) rules ---"
    grep -r 'ALL=(ALL)' /etc/sudoers /etc/sudoers.d/ 2>/dev/null | sed 's/^/  /'
    tool_audit "sudoers" "node=${NODE_ID}"
}

# --suid (SUID/SGID binary audit)
_do_suid_impl() {
    echo "=== SUID/SGID Audit: ${NODE_ID} ==="
    echo "--- SUID binaries ---"
    local suid_count; suid_count=$(find / -perm -4000 -type f 2>/dev/null | wc -l)
    echo "  Total SUID: ${suid_count}"
    echo "--- Unusual locations ---"
    find /opt /tmp /var/tmp /home /usr/local -perm -4000 -type f 2>/dev/null | while read -r f; do
        echo "  [WARN] ${f} ($(stat -c '%U:%G' "$f" 2>/dev/null))"
    done
    echo ""
    echo "--- SGID binaries in non-standard paths ---"
    find /opt /tmp /var/tmp /home /usr/local -perm -2000 -type f 2>/dev/null | while read -r f; do
        echo "  [WARN] ${f}"
    done
    tool_audit "suid" "suid_count=${suid_count} node=${NODE_ID}"
}

# --permissions (baseline check)
_do_permissions_impl() {
    echo "=== Permission Baseline: ${NODE_ID} ==="
    local issues=0
    # Critical directories
    for d in /etc/binary /opt/binary/keys /opt/binary/data; do
        if [ -d "$d" ]; then
            local perms; perms=$(stat -c '%a' "$d" 2>/dev/null)
            local owner; owner=$(stat -c '%U:%G' "$d" 2>/dev/null)
            echo "  ${d}: ${perms} (${owner})"
            # Check world-readable
            [ "${perms:2:1}" -ge 4 ] 2>/dev/null && { echo "    [WARN] World-readable"; issues=$((issues+1)); }
        fi
    done
    # Critical files
    for f in /etc/binary/hmac.key /etc/binary/tooling.env /etc/ssh/sshd_config; do
        if [ -f "$f" ]; then
            local perms; perms=$(stat -c '%a' "$f" 2>/dev/null)
            echo "  ${f}: ${perms}"
            [ "${perms:2:1}" -ge 4 ] 2>/dev/null && { echo "    [WARN] World-readable secret"; issues=$((issues+1)); }
        fi
    done
    echo ""
    echo "  Issues: ${issues}"
    tool_audit "permissions" "issues=${issues} node=${NODE_ID}"
}

# --log (audit log viewer)
_do_log_impl() {
    echo "=== Audit Log: ${NODE_ID} ==="
    if [ ! -f "$AUDIT_LOG" ]; then echo "  No audit log found at ${AUDIT_LOG}"; return 0; fi
    local lines; lines=$(wc -l < "$AUDIT_LOG" 2>/dev/null)
    local entries; entries=$(grep -c '^{' "$AUDIT_LOG" 2>/dev/null || echo "0")
    echo "  Log: ${AUDIT_LOG}"
    echo "  Lines: ${lines}  JSON entries: ${entries}"
    echo ""; echo "  Last 15 entries:"
    tail -15 "$AUDIT_LOG" 2>/dev/null | sed 's/^/    /'
}

# --fleet (aggregated fleet audit)
_do_fleet_impl() {
    echo "=== Fleet Security Audit ==="
    echo "  Running cross-node audit via fleet_exec..."
    echo ""
    if type fleet_exec &>/dev/null; then
        echo "--- Auth Verification ---"
        fleet_exec "security-auth.sh --verify" 2>/dev/null | sed 's/^/  /'
        echo ""
        echo "--- Intrusion Scan ---"
        fleet_exec "security-detect.sh --intrusion" 2>/dev/null | sed 's/^/  /'
        echo ""
        echo "--- Permission Check ---"
        fleet_exec "security-permissions.sh --send" 2>/dev/null | sed 's/^/  /'
    else
        echo "  [WARN] fleet_exec not available — running local only"
        _do_send_impl
    fi
    tool_audit "fleet" "node=${NODE_ID}"
}

# --verify
_do_verify_impl() {
    echo "=== Audit Verification: ${NODE_ID} ==="
    local pass=0 fail=0
    # Audit log exists and is writable
    if [ -f "$AUDIT_LOG" ]; then echo "  [OK] Audit log exists"; pass=$((pass+1))
    else echo "  [FAIL] Audit log missing"; fail=$((fail+1)); fi
    # No world-readable secrets
    local wr; wr=$(find /etc/binary /opt/binary/keys -maxdepth 2 -perm -o=r -type f 2>/dev/null | wc -l)
    if [ "$wr" -eq 0 ]; then echo "  [OK] No world-readable secrets"; pass=$((pass+1))
    else echo "  [FAIL] ${wr} world-readable secret(s)"; fail=$((fail+1)); fi
    # No extra UID 0
    local uid0; uid0=$(awk -F: '$3==0 && $1!="root"{print $1}' /etc/passwd 2>/dev/null)
    if [ -z "$uid0" ]; then echo "  [OK] No extra UID 0"; pass=$((pass+1))
    else echo "  [FAIL] Extra UID 0: ${uid0}"; fail=$((fail+1)); fi
    # No empty passwords
    local empty; empty=$(awk -F: '$2=="" {print $1}' /etc/shadow 2>/dev/null | wc -l)
    if [ "$empty" -eq 0 ]; then echo "  [OK] No empty passwords"; pass=$((pass+1))
    else echo "  [FAIL] ${empty} empty password(s)"; fail=$((fail+1)); fi
    echo ""
    echo "  Passed: ${pass}  Failed: ${fail}"
    tool_audit "verify" "pass=${pass} fail=${fail} node=${NODE_ID}"
    return $fail
}

_do_send_impl() {
    echo "=== Security Audit: ${NODE_ID} ==="
    _do_accounts_impl; echo ""
    _do_suid_impl; echo ""
    _do_permissions_impl; echo ""
    _do_log_impl
    tool_audit "send" "node=${NODE_ID}"
}

_do_status_impl() {
    local s="healthy"
    local wr; wr=$(find /etc/binary /opt/binary/keys -maxdepth 2 -perm -o=r -type f 2>/dev/null | wc -l)
    [ "$wr" -gt 0 ] && s="degraded"
    echo "{\"tool\":\"${TOOL_NAME}\",\"node\":\"${NODE_ID}\",\"status\":\"${s}\",\"world_readable\":${wr},\"checked_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
}

_do_digest_impl() { echo "=== Full Audit Report: ${NODE_ID} ==="; _do_send_impl; echo ""; _do_keys_impl; echo ""; _do_sudoers_impl; }
_do_flush_impl() {
    if [ -f "$AUDIT_LOG" ]; then
        local bak="${AUDIT_LOG}.$(date +%Y%m%d)"
        cp "$AUDIT_LOG" "$bak" 2>/dev/null
        truncate -s 0 "$AUDIT_LOG"
        echo "  [CHANGED] Rotated audit log -> ${bak}"
    fi
    [ -f "$TOOL_LOG" ] && truncate -s 0 "$TOOL_LOG"
    echo "Flush complete"
}

tool_help() {
    echo "${TOOL_NAME} v${TOOL_VERSION} -- Security Audit Tool"
    echo ""
    echo "Audit:"
    echo "  --accounts        Stale/orphaned account scan"
    echo "  --keys            SSH key audit"
    echo "  --sudoers         Sudoers drift check"
    echo "  --suid            SUID/SGID binary audit"
    echo "  --permissions     Permission baseline"
    echo "  --log             Audit log viewer"
    echo ""
    echo "Fleet:"
    echo "  --fleet           Fleet-wide aggregated audit"
    echo ""
    echo "Verify:"
    echo "  --verify          Full verification pass"
    echo "  --status          JSON pill"
    echo ""
    echo "Standard: --send|--digest|--flush"
}

do_send()   { tool_wrap "send"   _do_send_impl "$@"; }
do_status() { tool_wrap "status" _do_status_impl "$@"; }
do_digest() { tool_wrap "digest" _do_digest_impl "$@"; }
do_flush()  { tool_wrap "flush"  _do_flush_impl "$@"; }

tool_init; trap 'tool_complete' EXIT
case "${1:---help}" in
    --send) shift; do_send "$@" ;; --status) shift; do_status "$@" ;;
    --digest) shift; do_digest "$@" ;; --flush) shift; do_flush "$@" ;;
    --accounts) tool_wrap "accounts" _do_accounts_impl ;;
    --keys) tool_wrap "keys" _do_keys_impl ;;
    --sudoers) tool_wrap "sudoers" _do_sudoers_impl ;;
    --suid) tool_wrap "suid" _do_suid_impl ;;
    --permissions) tool_wrap "permissions" _do_permissions_impl ;;
    --log) tool_wrap "log" _do_log_impl ;;
    --fleet) tool_wrap "fleet" _do_fleet_impl ;;
    --verify) tool_wrap "verify" _do_verify_impl ;;
    --help) tool_help ;; *) handle_standard_command "$@" ;;
esac
