#!/usr/bin/env python3
"""
Vibe Pentest — Phase 7: 主机漏洞扫描包装器（不是所有品牌的漏扫都能使用此脚本）

调用 vuln_scan.py 执行主机/端口层漏洞扫描。
从 workspace 自动提取目标 host/port 和 scanner 配置。

用法:
  python run_vuln_scan.py --workspace <workspace_dir>
  python run_vuln_scan.py --workspace <dir> --host <ip> --port <port>
  python run_vuln_scan.py --workspace <dir> --scanner-url <url> --scanner-token <token>
"""

import argparse
import json
import shutil
import subprocess
import sys
import time
from pathlib import Path
from urllib.parse import urlparse


def parse_target_url(url: str) -> tuple:
    """从 URL 解析 host 和 port。"""
    parsed = urlparse(url)
    host = parsed.hostname
    port = parsed.port
    if not port:
        port = 443 if parsed.scheme == "https" else 80
    return host, port


def load_workspace(workspace: Path) -> dict:
    """加载 workspace 中的 session.json 和 fingerprint.json。"""
    session = {}
    session_path = workspace / "session.json"
    if session_path.exists():
        with open(session_path, "r", encoding="utf-8") as f:
            session = json.load(f)

    fingerprint = {}
    fp_path = workspace / "fingerprint.json"
    if fp_path.exists():
        with open(fp_path, "r", encoding="utf-8") as f:
            fingerprint = json.load(f)

    return session, fingerprint


def find_vuln_scan() -> str:
    """查找 vuln_scan.py 的路径。"""
    # 1. 脚本同级目录
    script_dir = Path(__file__).parent
    if (script_dir / "vuln_scan.py").exists():
        return str(script_dir / "vuln_scan.py")
    # 2. 当前工作目录
    cwd = Path.cwd()
    if (cwd / "vuln_scan.py").exists():
        return str(cwd / "vuln_scan.py")
    # 3. vibe-pentest skill 目录
    skill_dir = script_dir.parent  # vibe-pentest/
    for ext in ["", ".py"]:
        p = skill_dir / f"vuln_scan{ext}"
        if p.exists():
            return str(p)
    print("[ERROR] 找不到 vuln_scan.py", file=sys.stderr)
    sys.exit(1)


def main():
    parser = argparse.ArgumentParser(
        description="Vibe Pentest Phase 7 — 主机漏洞扫描",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  python run_vuln_scan.py --workspace ./workspace
  python run_vuln_scan.py --workspace ./workspace --host 192.168.1.33 --port 8843
  python run_vuln_scan.py --workspace ./workspace --scanner-url https://192.168.0.100:23000 --scanner-token xxx
        """,
    )
    parser.add_argument("--workspace", "-w", required=True, help="vibe-pentest workspace 目录")
    parser.add_argument("--host", "-H", help="目标 IP（不指定则从 workspace 自动提取）")
    parser.add_argument("--port", "-p", help="目标端口（不指定则从 workspace 自动提取）")
    parser.add_argument("--scanner-url", "-u", help="漏扫系统 API 地址")
    parser.add_argument("--scanner-token", "-t", help="漏扫系统 access_token")
    parser.add_argument("--output-dir", "-o", help="报告输出目录（默认: workspace）")
    parser.add_argument("--user-id", type=int, default=1, help="用户 ID（默认: 1）")
    parser.add_argument("--all-port", action="store_true", help="全端口扫描")
    parser.add_argument("--need-pw", action="store_true", help="开启弱口令扫描")
    parser.add_argument("--no-poc", action="store_true", help="关闭 POC 扫描")
    parser.add_argument("--no-cpe", action="store_true", help="关闭 CPE 扫描")

    args = parser.parse_args()

    workspace = Path(args.workspace)
    if not workspace.exists():
        print(f"[ERROR] workspace 不存在: {workspace}", file=sys.stderr)
        sys.exit(1)

    # ---- 加载 workspace 配置 ----
    print(f"[*] 加载 workspace: {workspace}")
    session, fingerprint = load_workspace(workspace)

    # ---- 获取目标 host/port ----
    host = args.host
    port = args.port

    if not host or not port:
        target_url = session.get("target_url", "")
        if target_url:
            auto_host, auto_port = parse_target_url(target_url)
            if not host:
                host = auto_host
                print(f"  [*] 自动提取 host: {host}")
            if not port:
                port = str(auto_port)
                print(f"  [*] 自动提取 port: {port}")
        else:
            print("[ERROR] 无法获取目标 host/port，请通过 --host --port 指定", file=sys.stderr)
            sys.exit(1)

    # ---- 获取 scanner 配置 ----
    scanner_url = args.scanner_url
    scanner_token = args.scanner_token

    if not scanner_url or not scanner_token:
        scanner_config = session.get("scanner", {})
        if scanner_config:
            if not scanner_url:
                scanner_url = scanner_config.get("api_url", "")
                print(f"  [*] 从 session.json 获取 scanner URL: {scanner_url}")
            if not scanner_token:
                scanner_token = scanner_config.get("access_token", "")
                print(f"  [*] 从 session.json 获取 access_token")
        else:
            print("[ERROR] 未配置漏扫系统 (scanner_url/scanner_token)，跳过主机扫描", file=sys.stderr)
            print(f"[*] 如需启用，请在 session.json 中添加 scanner 字段，或使用 --scanner-url --scanner-token 参数", file=sys.stderr)
            return

    if not scanner_url or not scanner_token:
        print("[ERROR] scanner URL 或 token 为空，跳过主机扫描", file=sys.stderr)
        return

    # ---- 输出目录 ----
    output_dir = args.output_dir or str(workspace)
    Path(output_dir).mkdir(parents=True, exist_ok=True)

    # ---- 构建命令 ----
    vuln_scan_py = find_vuln_scan()
    print(f"  [*] 使用脚本: {vuln_scan_py}")

    cmd = [
        sys.executable, vuln_scan_py,
        "-H", host,
        "-p", str(port),
        "-u", scanner_url,
        "-t", scanner_token,
        "--user-id", str(args.user_id),
        "-o", output_dir,
    ]

    if args.all_port:
        cmd.append("--all-port")
    if args.need_pw:
        cmd.append("--need-pw")
    if args.no_poc:
        cmd.append("--no-poc")
    if args.no_cpe:
        cmd.append("--no-cpe")

    print(f"\n{'=' * 52}")
    print(f"Phase 7: 主机漏洞扫描")
    print(f"  目标:   {host}:{port}")
    print(f"  扫描器: {scanner_url}")
    print(f"  输出:   {output_dir}")
    print(f"{'=' * 52}\n")

    # ---- 执行扫描 ----
    start_time = time.time()
    try:
        result = subprocess.run(cmd, cwd=str(workspace), timeout=None)
        elapsed = time.time() - start_time

        if result.returncode != 0:
            print(f"\n[!] 漏扫脚本退出码: {result.returncode}")
            print(f"[*] 扫描耗时: {elapsed:.0f}s")
            return

        print(f"\n[+] 扫描完成，耗时: {elapsed:.0f}s")

        # ---- 查找下载的 zip 文件 ----
        zip_files = list(Path(output_dir).glob(f"scan_{host}_{port}_*.zip"))
        if zip_files:
            latest = max(zip_files, key=lambda p: p.stat().st_mtime)
            dest = workspace / "vuln_scan_report.zip"
            shutil.copy(str(latest), str(dest))
            print(f"[+] 报告已复制到: {dest}")
            print(f"    原始文件: {latest} ({latest.stat().st_size / 1024:.0f} KB)")
        else:
            print(f"[!] 未找到下载的 zip 文件，检查 {output_dir} 目录")

    except subprocess.TimeoutExpired:
        print("[!] 扫描超时")
    except KeyboardInterrupt:
        print("\n[!] 用户中断")
    except Exception as e:
        print(f"[!] 扫描失败: {e}")


if __name__ == "__main__":
    main()
