#!/usr/bin/env bash
# 내 디지털 발자국 진단 — WSL2/Linux 로컬 보안 점검 스크립트
# https://findperson.usefulinfoapp.com/guide/local-security-check
#
# 읽기 전용입니다. 설정을 바꾸거나 무언가를 설치·삭제하지 않습니다.
# "실행 중인 프로그램이 알려진 악성코드인지" 확인하는 항목만 예외적으로
# 외부(abuse.ch)에서 최근 악성코드 해시 목록을 받아옵니다 — 받아오기만 하고,
# 이 컴퓨터의 어떤 정보도 그쪽으로 보내지 않습니다. 나머지 점검은 전부
# 이 컴퓨터 안에서만 이루어집니다.
#
# 실행 전에 이 파일을 열어 내용을 직접 확인하시길 권합니다.
set -uo pipefail

GREEN='\033[0;32m'; YELLOW='\033[0;33m'; RED='\033[0;31m'; NC='\033[0m'

# ── 결과를 모아 뒀다가 마지막에 보기 좋은 보고서로 다시 뽑는다 ──────────
FINDINGS_FILE=$(mktemp)
trap 'rm -f "$FINDINGS_FILE"' EXIT

record() {
  local level="$1" category="$2" message="$3"
  echo "${level}|${category}|${message}" >> "$FINDINGS_FILE"
  case "$level" in
    ok)   echo -e "  ${GREEN}[양호]${NC} $message" ;;
    warn) echo -e "  ${YELLOW}[주의]${NC} $message" ;;
    bad)  echo -e "  ${RED}[확인 필요]${NC} $message" ;;
  esac
}
ok()   { record ok "$1" "$2"; }
warn() { record warn "$1" "$2"; }
bad()  { record bad "$1" "$2"; }

echo "== WSL2 / Linux 로컬 보안 점검 =========================="
echo "쉬운 말로 풀어 설명합니다. 전문 용어는 괄호 안에 원래 이름을 같이 적었습니다."

echo; echo "-- 시스템이 최신 상태로 패치돼 있는지 (apt 패키지) --"
if command -v apt >/dev/null 2>&1; then
  upd=$(apt list --upgradable 2>/dev/null | grep -vc "^Listing")
  if [ "${upd:-0}" -gt 0 ]; then
    warn "패키지 업데이트" "업데이트 가능한 패키지 ${upd}개가 있습니다. 실행: sudo apt update && sudo apt upgrade"
  else
    ok "패키지 업데이트" "패키지가 최신 상태입니다 (또는 apt update를 아직 안 돌렸을 수 있습니다)."
  fi
else
  warn "패키지 업데이트" "apt를 찾을 수 없어 이 검사는 건너뜁니다 (다른 배포판일 수 있습니다)."
fi

echo; echo "-- 외부와 연결을 기다리고 있는 통로가 있는지 (열려 있는 포트) --"
if command -v ss >/dev/null 2>&1; then
  ss -tulpn 2>/dev/null | awk 'NR==1 || /LISTEN|UNCONN/' | sed 's/^/  /'
  ok "포트" "위 목록에서 낯선 프로그램이 있으면 이름을 검색해 확인하세요."
else
  warn "포트" "ss 명령이 없어 포트 확인을 건너뜁니다."
fi

echo; echo "-- 원격 접속(SSH) 열쇠가 안전하게 보관돼 있는지 --"
if [ -d "$HOME/.ssh" ]; then
  perm=$(stat -c '%a' "$HOME/.ssh" 2>/dev/null)
  [ "$perm" = "700" ] && ok "SSH" "~/.ssh 폴더 권한이 안전합니다 (700)." \
    || bad "SSH" "~/.ssh 폴더 권한이 $perm 입니다. 다른 사용자가 들여다볼 수 있습니다 — 고치기: chmod 700 ~/.ssh"
  if [ -f "$HOME/.ssh/authorized_keys" ]; then
    n=$(wc -l < "$HOME/.ssh/authorized_keys")
    warn "SSH" "이 컴퓨터에 원격 접속 가능한 공개키가 ${n}개 등록돼 있습니다. 모르는 키가 있는지 확인하세요."
  fi
  for f in "$HOME"/.ssh/id_* ; do
    [ -e "$f" ] || continue
    case "$f" in *.pub) continue;; esac
    p=$(stat -c '%a' "$f" 2>/dev/null)
    [ "$p" = "600" ] || bad "SSH" "개인키 $f 권한이 $p 입니다 — 고치기: chmod 600 $f"
  done
else
  ok "SSH" "~/.ssh 폴더가 없습니다."
fi

echo; echo "-- 정해진 시간마다 자동으로 실행되게 등록해 둔 작업이 있는지 (crontab) --"
cron_lines=$(crontab -l 2>/dev/null | grep -v '^#' | grep -v '^$')
if [ -n "$cron_lines" ]; then
  echo "$cron_lines" | sed 's/^/  /'
  warn "예약 작업" "등록된 작업이 있습니다. 본인이 등록하지 않은 것이라면 확인이 필요합니다."
else
  ok "예약 작업" "등록된 예약 작업이 없습니다."
fi

echo; echo "-- 셸이 열릴 때마다 자동으로 실행되는 설정 파일이 최근에 바뀌었는지 --"
for f in "$HOME/.bashrc" "$HOME/.profile" "$HOME/.bash_profile"; do
  [ -f "$f" ] && stat -c "  %y  %n" "$f" 2>/dev/null
done
warn "시작 스크립트" "본인이 최근에 고친 적이 없는데 날짜가 최근이면 내용을 열어 확인하세요."

echo; echo "-- 아무나 고칠 수 있게 열려 있는 파일이 있는지 (world-writable) --"
n=$(find "$HOME" -maxdepth 4 -type f -perm -o+w 2>/dev/null | wc -l)
if [ "$n" -gt 0 ]; then
  warn "파일 권한" "홈 디렉터리 안에 아무나 고칠 수 있는 파일이 ${n}개 있습니다."
else
  ok "파일 권한" "아무나 고칠 수 있는 파일이 없습니다."
fi

echo; echo "-- 지금 실행 중인 프로그램이 최근 알려진 악성코드와 같은 것인지 --"
echo "  abuse.ch(보안 연구 커뮤니티)가 공개한 최근 악성코드 목록을 받아 옵니다."
echo "  받아오기만 합니다 — 이 컴퓨터의 어떤 정보도 밖으로 나가지 않습니다."
if command -v curl >/dev/null 2>&1 && (command -v sha256sum >/dev/null 2>&1); then
  IOC_FILE=$(mktemp)
  if curl -fsSL --max-time 20 'https://bazaar.abuse.ch/export/txt/sha256/recent/' -o "$IOC_FILE" 2>/dev/null; then
    # abuse.ch 파일은 CRLF 줄바꿈이라, \r을 먼저 지우지 않으면 $ 앵커가
    # 매칭되지 않아 해시를 하나도 못 건진다.
    tr -d '\r' < "$IOC_FILE" | grep -Eo '^[0-9a-fA-F]{64}$' | tr 'A-F' 'a-f' | sort -u > "${IOC_FILE}.clean"
    ioc_count=$(wc -l < "${IOC_FILE}.clean")
    echo "  최근 악성코드 해시 ${ioc_count}개를 받았습니다. 지금 실행 중인 프로그램과 대조합니다..."

    exe_paths=$(for p in /proc/[0-9]*/exe; do readlink -f "$p" 2>/dev/null; done | sort -u)
    matched=0
    checked=0
    while IFS= read -r exe; do
      [ -n "$exe" ] || continue
      [ -r "$exe" ] || continue
      h=$(sha256sum "$exe" 2>/dev/null | awk '{print $1}')
      [ -n "$h" ] || continue
      checked=$((checked + 1))
      if grep -qx "$h" "${IOC_FILE}.clean"; then
        bad "악성코드 대조" "실행 중인 파일이 최근 공개된 악성코드 해시와 일치합니다: $exe — 즉시 확인하고 필요하면 프로세스를 종료·삭제하세요."
        matched=$((matched + 1))
      fi
    done <<< "$exe_paths"

    [ "$matched" -eq 0 ] && ok "악성코드 대조" "실행 중인 프로그램 ${checked}개 중 최근 악성코드 목록과 일치하는 것은 없습니다."
    echo "  참고: 이 목록은 '최근' 것만 담고 있어 오래된 악성코드는 못 잡습니다. 백신을 대신하지 않습니다."
  else
    warn "악성코드 대조" "악성코드 목록을 받아오지 못해 이 항목은 건너뜁니다. (인터넷 연결을 확인해 주세요.)"
  fi
  rm -f "$IOC_FILE" "${IOC_FILE}.clean"
else
  warn "악성코드 대조" "curl 또는 sha256sum 명령이 없어 이 항목은 건너뜁니다."
fi

# ── 요약 보고서 ──────────────────────────────────────────────────────
echo; echo
echo "===================== 요약 보고서 ====================="
bad_n=$(grep -c '^bad|' "$FINDINGS_FILE" || true)
warn_n=$(grep -c '^warn|' "$FINDINGS_FILE" || true)
ok_n=$(grep -c '^ok|' "$FINDINGS_FILE" || true)
total=$((bad_n + warn_n + ok_n))
echo "총 ${total}개 항목 점검 — 확인 필요 ${bad_n}개 · 주의 ${warn_n}개 · 양호 ${ok_n}개"
echo

if [ "$bad_n" -gt 0 ]; then
  echo -e "${RED}■ 지금 바로 조치하세요${NC}"
  grep '^bad|' "$FINDINGS_FILE" | while IFS='|' read -r _ cat msg; do
    echo -e "  ${RED}·${NC} [$cat] $msg"
  done
  echo
fi
if [ "$warn_n" -gt 0 ]; then
  echo -e "${YELLOW}■ 알아 두고 판단하세요${NC}"
  grep '^warn|' "$FINDINGS_FILE" | while IFS='|' read -r _ cat msg; do
    echo -e "  ${YELLOW}·${NC} [$cat] $msg"
  done
  echo
fi
if [ "$bad_n" -eq 0 ] && [ "$warn_n" -eq 0 ]; then
  echo -e "${GREEN}모든 항목이 양호합니다.${NC}"
fi
echo "=========================================================="
echo "이 결과는 화면에만 표시되며 어디에도 저장·전송되지 않습니다."
echo "이 점검은 백신을 대신하지 않습니다 — 설정 상태와 최근 악성코드 해시 일치 여부만 보여줍니다."
