#!/bin/bash

#######################################################################
# Sha1-Hulud 2.0 NPM Supply Chain Attack Scanner
#
# Downloads real-time infected package list from Koi.ai and scans your project
#
# Usage: ./scan.sh [project_path]
#        ./scan.sh                    # Scan current directory
#        ./scan.sh /path/to/project   # Scan specific project
#######################################################################

set -e

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
BOLD='\033[1m'

# Configuration
KOI_CSV_URL="https://docs.google.com/spreadsheets/d/16aw6s7mWoGU7vxBciTEZSaR5HaohlBTfVirvI-PypJc/export?format=csv&gid=1289659284"
CACHE_DIR="${HOME}/.cache/sha1-hulud-scanner"
CACHE_FILE="${CACHE_DIR}/compromised_packages.csv"
CACHE_TTL=3600  # 1 hour cache

# Result counters
TOTAL_CHECKS=0
INFECTED_COUNT=0
WARNING_COUNT=0
declare -a INFECTED_PACKAGES
declare -a WARNINGS

#######################################################################
# Utility Functions
#######################################################################

print_banner() {
    echo -e "${CYAN}"
    echo "╔═══════════════════════════════════════════════════════════════╗"
    echo "║        🐛 Sha1-Hulud 2.0 Supply Chain Attack Scanner         ║"
    echo "║                                                               ║"
    echo "║  Data Source: Koi.ai Live Updates                            ║"
    echo "║  https://www.koi.ai/incident/live-updates-sha1-hulud         ║"
    echo "╚═══════════════════════════════════════════════════════════════╝"
    echo -e "${NC}"
}

log_info() {
    echo -e "${BLUE}[ℹ]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[✓]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[⚠]${NC} $1"
    ((WARNING_COUNT++))
    WARNINGS+=("$1")
}

log_error() {
    echo -e "${RED}[✗]${NC} $1"
}

log_infected() {
    echo -e "${RED}[🚨 INFECTED]${NC} $1"
    ((INFECTED_COUNT++))
    INFECTED_PACKAGES+=("$1")
}

#######################################################################
# Cache Management
#######################################################################

ensure_cache_dir() {
    mkdir -p "${CACHE_DIR}"
}

is_cache_valid() {
    if [[ ! -f "${CACHE_FILE}" ]]; then
        return 1
    fi

    local cache_age=$(($(date +%s) - $(stat -f %m "${CACHE_FILE}" 2>/dev/null || stat -c %Y "${CACHE_FILE}" 2>/dev/null)))

    if [[ ${cache_age} -gt ${CACHE_TTL} ]]; then
        return 1
    fi

    return 0
}

#######################################################################
# Download Compromised Package List
#######################################################################

download_compromised_list() {
    log_info "Downloading latest compromised package list from Koi.ai..."

    ensure_cache_dir

    if is_cache_valid; then
        log_info "Using cached list (downloaded within 1 hour)"
        return 0
    fi

    if command -v curl &> /dev/null; then
        curl -sL "${KOI_CSV_URL}" -o "${CACHE_FILE}" 2>/dev/null
    elif command -v wget &> /dev/null; then
        wget -q "${KOI_CSV_URL}" -O "${CACHE_FILE}" 2>/dev/null
    else
        log_error "curl or wget is required"
        exit 1
    fi

    if [[ ! -s "${CACHE_FILE}" ]]; then
        log_error "Failed to download compromised package list"
        exit 1
    fi

    local count=$(wc -l < "${CACHE_FILE}" | tr -d ' ')
    log_success "Downloaded compromised package list (${count} entries)"
}

#######################################################################
# Package Scanning
#######################################################################

check_package_lock() {
    local project_path="$1"
    local lock_file=""

    # Find package-lock.json, yarn.lock, or pnpm-lock.yaml
    if [[ -f "${project_path}/package-lock.json" ]]; then
        lock_file="${project_path}/package-lock.json"
        log_info "Scanning package-lock.json..."
    elif [[ -f "${project_path}/yarn.lock" ]]; then
        lock_file="${project_path}/yarn.lock"
        log_info "Scanning yarn.lock..."
    elif [[ -f "${project_path}/pnpm-lock.yaml" ]]; then
        lock_file="${project_path}/pnpm-lock.yaml"
        log_info "Scanning pnpm-lock.yaml..."
    else
        log_warning "No lock file found. Scanning package.json only."
        return
    fi

    # Extract package name and version from CSV and check
    while IFS=, read -r package_name version status date rest; do
        # Skip header
        [[ "${package_name}" == "package"* ]] && continue
        [[ -z "${package_name}" ]] && continue

        # Remove quotes
        package_name=$(echo "${package_name}" | tr -d '"')
        version=$(echo "${version}" | tr -d '"')

        ((TOTAL_CHECKS++))

        # Search for package in lock file
        if grep -q "\"${package_name}\"" "${lock_file}" 2>/dev/null || \
           grep -q "${package_name}@${version}" "${lock_file}" 2>/dev/null; then

            # Check exact version match
            if grep -q "\"${package_name}\": \"${version}\"" "${lock_file}" 2>/dev/null || \
               grep -q "\"${package_name}\": \"^${version}\"" "${lock_file}" 2>/dev/null || \
               grep -q "${package_name}@${version}" "${lock_file}" 2>/dev/null; then
                log_infected "${package_name}@${version}"
            elif grep -q "\"${package_name}\"" "${lock_file}" 2>/dev/null; then
                log_warning "${package_name} found - manual version check required"
            fi
        fi
    done < "${CACHE_FILE}"
}

check_node_modules() {
    local project_path="$1"
    local node_modules="${project_path}/node_modules"

    if [[ ! -d "${node_modules}" ]]; then
        log_info "node_modules directory not found - skipping"
        return
    fi

    log_info "Direct node_modules scan..."

    while IFS=, read -r package_name version status date rest; do
        [[ "${package_name}" == "package"* ]] && continue
        [[ -z "${package_name}" ]] && continue

        package_name=$(echo "${package_name}" | tr -d '"')
        version=$(echo "${version}" | tr -d '"')

        # Handle scoped packages (@org/package)
        local pkg_path="${node_modules}/${package_name}"

        if [[ -d "${pkg_path}" ]]; then
            local installed_version=$(node -p "require('${pkg_path}/package.json').version" 2>/dev/null || echo "")

            if [[ "${installed_version}" == "${version}" ]]; then
                log_infected "${package_name}@${version} (found in node_modules)"
            fi
        fi
    done < "${CACHE_FILE}"
}

#######################################################################
# IOC (Indicator of Compromise) Scanning
#######################################################################

check_ioc_files() {
    local project_path="$1"

    echo ""
    log_info "Scanning for IOC files..."

    # Malicious script files
    local malicious_files=(
        "setup_bun.js"
        "bun_environment.js"
        "cloud.json"
        "contents.json"
        "environment.json"
        "truffleSecrets.json"
    )

    for file in "${malicious_files[@]}"; do
        local found=$(find "${project_path}" -name "${file}" -not -path "*/\.*" 2>/dev/null)
        if [[ -n "${found}" ]]; then
            log_infected "Malicious file found: ${found}"
        fi
    done

    # Check suspicious patterns in preinstall scripts
    log_info "Scanning for suspicious preinstall scripts..."

    if [[ -d "${project_path}/node_modules" ]]; then
        local suspicious=$(grep -r '"preinstall"' "${project_path}/node_modules"/*/package.json 2>/dev/null | grep -iE "bun|setup|curl|wget|eval" || true)
        if [[ -n "${suspicious}" ]]; then
            log_warning "Suspicious preinstall script found:"
            echo "${suspicious}" | head -10
        fi
    fi

    # Check malicious domain references
    log_info "Scanning for malicious domain references..."

    local malicious_domains=(
        "packages.storeartifact.com"
        "hulud"
    )

    for domain in "${malicious_domains[@]}"; do
        local found=$(grep -r "${domain}" "${project_path}" --include="*.js" --include="*.json" 2>/dev/null | head -5 || true)
        if [[ -n "${found}" ]]; then
            log_infected "Malicious domain reference found: ${domain}"
            echo "${found}"
        fi
    done
}

check_github_actions() {
    local project_path="$1"
    local workflows_dir="${project_path}/.github/workflows"

    echo ""
    log_info "Scanning GitHub Actions for infection..."

    if [[ ! -d "${workflows_dir}" ]]; then
        log_info ".github/workflows directory not found - skipping"
        return
    fi

    # Check for malicious workflow files
    local hulud_workflows=$(find "${workflows_dir}" -name "*hulud*" -o -name "*shai*" 2>/dev/null || true)
    if [[ -n "${hulud_workflows}" ]]; then
        log_infected "Malicious workflow file found:"
        echo "${hulud_workflows}"
    fi

    # Check for suspicious content in workflows
    local suspicious_content=$(grep -r -l "hulud\|storeartifact\|credential.*harvest" "${workflows_dir}" 2>/dev/null || true)
    if [[ -n "${suspicious_content}" ]]; then
        log_infected "Suspicious content found in workflows:"
        echo "${suspicious_content}"
    fi

    # Check for suspicious branches
    if [[ -d "${project_path}/.git" ]]; then
        local hulud_branches=$(cd "${project_path}" && git branch -a 2>/dev/null | grep -i hulud || true)
        if [[ -n "${hulud_branches}" ]]; then
            log_infected "Suspicious Git branch found:"
            echo "${hulud_branches}"
        fi
    fi
}

#######################################################################
# Result Report
#######################################################################

print_report() {
    echo ""
    echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
    echo -e "${BOLD}                         SCAN REPORT                            ${NC}"
    echo -e "${BOLD}═══════════════════════════════════════════════════════════════${NC}"
    echo ""

    echo -e "Packages checked:  ${TOTAL_CHECKS}"
    echo -e "Infected packages: ${RED}${INFECTED_COUNT}${NC}"
    echo -e "Warnings:          ${YELLOW}${WARNING_COUNT}${NC}"
    echo ""

    if [[ ${INFECTED_COUNT} -gt 0 ]]; then
        echo -e "${RED}${BOLD}🚨 INFECTION DETECTED! Immediate action required!${NC}"
        echo ""
        echo -e "${BOLD}Infected packages:${NC}"
        for pkg in "${INFECTED_PACKAGES[@]}"; do
            echo -e "  ${RED}•${NC} ${pkg}"
        done
        echo ""
        echo -e "${BOLD}Recommended actions:${NC}"
        echo "  1. Remove infected packages immediately or rollback to safe versions"
        echo "  2. Rotate npm tokens, GitHub PATs, SSH keys immediately"
        echo "  3. Rotate AWS/GCP/Azure cloud credentials"
        echo "  4. Review .github/workflows/ directory manually"
        echo "  5. Check git log for suspicious commits"
        echo ""
        echo -e "${YELLOW}Reference: https://www.koi.ai/incident/live-updates-sha1-hulud${NC}"
    elif [[ ${WARNING_COUNT} -gt 0 ]]; then
        echo -e "${YELLOW}${BOLD}⚠️  Warnings found. Manual verification required.${NC}"
        echo ""
        for warn in "${WARNINGS[@]}"; do
            echo -e "  ${YELLOW}•${NC} ${warn}"
        done
    else
        echo -e "${GREEN}${BOLD}✅ No infection detected!${NC}"
        echo ""
        echo "However, we recommend:"
        echo "  • Run this scanner regularly"
        echo "  • Run npm audit periodically"
        echo "  • Pin dependency versions (commit package-lock.json)"
    fi

    echo ""
    echo -e "${CYAN}Scan completed: $(date '+%Y-%m-%d %H:%M:%S')${NC}"
    echo -e "${CYAN}Data source: Koi.ai (${KOI_CSV_URL})${NC}"
}

#######################################################################
# Main
#######################################################################

main() {
    print_banner

    local project_path="${1:-.}"
    project_path=$(cd "${project_path}" 2>/dev/null && pwd)

    if [[ ! -d "${project_path}" ]]; then
        log_error "Project path not found: ${project_path}"
        exit 1
    fi

    log_info "Project path: ${project_path}"
    echo ""

    # Check for package.json
    if [[ ! -f "${project_path}/package.json" ]]; then
        log_error "package.json not found. This doesn't appear to be an npm project."
        exit 1
    fi

    # Download compromised list
    download_compromised_list
    echo ""

    # Run scans
    echo -e "${BOLD}[1/4] Package Lock File Scan${NC}"
    check_package_lock "${project_path}"

    echo ""
    echo -e "${BOLD}[2/4] Direct node_modules Scan${NC}"
    check_node_modules "${project_path}"

    echo ""
    echo -e "${BOLD}[3/4] IOC File Scan${NC}"
    check_ioc_files "${project_path}"

    echo ""
    echo -e "${BOLD}[4/4] GitHub Actions Scan${NC}"
    check_github_actions "${project_path}"

    # Output results
    print_report

    # Exit code 1 if infection found
    if [[ ${INFECTED_COUNT} -gt 0 ]]; then
        exit 1
    fi

    exit 0
}

main "$@"
