"""漏洞盒子「提交漏洞」表单填充器 — 严格对齐 vulbox.com 真实表单字段。

用法:
    python3 gen_report.py V-001 "title" critical \\
        --vuln-url "https://www.coloros.com/path" \\
        --vuln-type "反射型XSS" \\
        --desc "..." \\
        --reproduce-html "<p>步骤 1...</p>" \\
        --fix-html "<p>建议...</p>" \\
        --evidence-files "C:\\path\\01.png,C:\\path\\02.png"

输出: C:\\Users\\Administrator\\Desktop\\vuln_report_<task_id>.md
       + 剪贴板里复制 ready 的 HTML 富文本片段
       + 字段清单对照表（你手动复制到 vulbox 表单）
"""
import argparse, sys, subprocess
from datetime import datetime
from pathlib import Path

EXCEL_PATH = r"C:\Users\Administrator\srccrawler\渗透任务跟踪表.xlsx"

SEVERITY_MAP = {
    "critical": "严重", "high": "高危", "medium": "中危", "low": "低危",
    "严重": "严重", "高危": "高危", "中危": "中危", "低危": "低危",
}

# 漏洞盒子下拉框里的漏洞类型（部分常见值）
KNOWN_VULN_TYPES = [
    # XSS
    "反射型XSS", "存储型XSS", "DOM型XSS",
    # 注入
    "SQL注入", "命令注入", "CRLF注入",
    # 逻辑漏洞
    "逻辑漏洞",
    # 权限控制缺失
    "未授权访问", "平行越权", "垂直越权", "其他权限控制缺失",
    # 支付漏洞
    "支付漏洞",
    # 认证缺陷
    "密码重置", "任意注册", "任意登录", "撞库/扫号/暴力破解",
    # 弱口令
    "弱口令",
    # 条件竞争
    "条件竞争",
    # 代码执行
    "代码执行", "RCE", "远程代码执行",
    # 信息泄露
    "信息泄露", "敏感信息泄露",
    # 文件包含
    "文件包含", "LFI",
    # 任意文件操作
    "任意文件读取", "任意文件删除", "任意文件写入",
    # 上传漏洞
    "上传漏洞", "任意文件上传",
    # URL 重定向
    "URL重定向", "开放重定向",
    # XXE
    "XXE",
    # SSRF
    "SSRF",
    # CSRF
    "CSRF",
    # 安全配置错误
    "Thinkphp Debug配置错误", "目录遍历", "SPF邮箱伪造",
    "spring配置不当", "域名劫持", "CORS配置错误",
    "缺失安全响应头", "安全策略配置不当",
    # 第三方组件漏洞
    "心脏滴血", "IIS短文件名", "JAVA反序列化漏洞", "Tomcat CSRF",
    "编辑器文件上传漏洞", "Struts2 S2-xxx", "Spring4Shell",
    "log4j2", "shiro反序列化", "Shiro RememberMe",
    # 拒绝服务
    "拒绝服务漏洞", "DoS",
    # 暴力破解
    "暴力破解",
    # 并发
    "并发", "并发竞争",
    # 兜底
    "其他", "其他已知漏洞", "其他未知漏洞",
]


def severity_zh(s):
    s = s.lower()
    return SEVERITY_MAP.get(s, SEVERITY_MAP.get(s, "严重"))


def get_project_info(task_id):
    """从 Excel 查项目名（vulbox 项目显示名）"""
    try:
        from openpyxl import load_workbook
        wb = load_workbook(EXCEL_PATH, read_only=True)
        ws = wb["任务清单"]
        for row in ws.iter_rows(min_row=5, values_only=True):
            if row[0] == task_id:
                return row[2] or "(未命名)"
    except Exception as e:
        print(f"[warn] Excel 读取失败: {e}")
    return "(未找到)"


def build_report(args):
    project = get_project_info(args.task_id)
    today = datetime.now().strftime("%Y-%m-%d")
    sev_zh = severity_zh(args.severity)

    # 字段 1: 项目名称（自动从 Excel 读）
    f_project = project

    # 字段 2: 漏洞标题（用户输入 + 前缀）
    title_prefix = ""  # 不强加，让用户自己写
    f_title = args.title

    # 字段 3: 漏洞域名
    f_url = args.vuln_url or ""

    # 字段 4: 漏洞类型
    f_type = args.vuln_type if args.vuln_type in KNOWN_VULN_TYPES else "其他"

    # 字段 5: 漏洞等级（单选）
    f_severity = sev_zh

    # 字段 6: 漏洞简述
    f_desc = args.desc or "待补充：漏洞概况及危害说明"

    # 字段 7: 复现步骤（HTML）
    f_repro = args.reproduce_html or "<p>待补充：分步骤图文描述复现步骤</p>"

    # 字段 8: 修复建议（HTML）
    f_fix = args.fix_html or "<p>待补充：具体修复方案与代码示例</p>"

    # 字段 9: 文件上传（脚本仅打印路径清单，不实际复制）
    files = [f.strip() for f in (args.evidence_files or "").split(",") if f.strip()]
    file_list_md = "\n".join(f"- `{Path(f).name}` ({Path(f)})" for f in files) if files else "（未提供截图，需手动附加）"

    # 字段 10: 用户协议（不勾选，留给用户手动）

    md = f"""# 漏洞盒子「提交漏洞」表单填充清单

> **任务 ID**: {args.task_id}
> **项目**: {project}
> **生成时间**: {datetime.now().isoformat(timespec='seconds')}
> **下一步**: 复制下面 HTML 到漏洞盒子提交页 → 勾「同意协议」→ 点「提交漏洞」

---

## 📋 字段对照（按漏洞盒子表单顺序）

| # | 字段 | 类型 | 值 |
|---|---|---|---|
| 1 | 项目名称 | 输入框 | `{f_project}` |
| 2 | 漏洞标题 | 输入框 | `{f_title}` |
| 3 | 漏洞域名 | 输入框 | `{f_url}` |
| 4 | 漏洞类型 | 下拉框 | `{f_type}` |
| 5 | 漏洞等级 | 单选 | `{f_severity}` ⬅ 点选 |
| 6 | 漏洞简述 | 多行文本 | 见下方 |
| 7 | 复现步骤 | **HTML 富文本** | 见下方 HTML |
| 8 | 修复建议 | **HTML 富文本** | 见下方 HTML |
| 9 | 文件上传 | 文件框 | {len(files)} 个文件（路径见附录） |
| 10 | 用户协议 | 复选框 | **手动勾选** |
| 11 | 提交漏洞 | 按钮 | **手动点击** |

---

## 6️⃣ 漏洞简述（多行文本，复制此段）

```
{f_desc}
```

---

## 7️⃣ 复现步骤 HTML（粘贴到富文本编辑器「HTML 模式」）

```html
{f_repro}
```

---

## 8️⃣ 修复建议 HTML（粘贴到富文本编辑器「HTML 模式」）

```html
{f_fix}
```

---

## 📎 附件清单（手动拖到「文件上传」框）

{file_list_md}

---

## ⚠️ 提交前自检清单

- [ ] 漏洞标题前缀（**部分 SRC 要求**）：OPPO 加 `[OPPO]` / vivo 加 `[vivo]` / PayPal 加 `[PPSRC]`（具体看 SRC 公告）
- [ ] 漏洞域名是否在**资产清单**（`C:\\Users\\Administrator\\srccrawler\\渗透任务跟踪表.xlsx` 的「资产清单」sheet）
- [ ] 截图是否覆盖 3 类：①异常响应 ②实际利用成功 ③影响范围
- [ ] 复现步骤是否带具体 curl / sqlmap / burp 命令（不要只写"手工测试"）
- [ ] 修复建议是否给代码 diff，不是"加 WAF"这种空话
- [ ] 「同意《漏洞盒子平台用户协议》」**勾上**
- [ ] 「提交漏洞」or「预存」（预存=草稿箱，不会真提交）

---

## 📊 回填到 Excel（提交成功后）

`C:\\Users\\Administrator\\srccrawler\\渗透任务跟踪表.xlsx` 的「漏洞追踪」sheet：

| 发现日期 | 任务ID | SRC平台 | 项目 | 漏洞名 | 严重等级 | 状态 | 奖励金额 |
|---|---|---|---|---|---|---|---|
| {today} | {args.task_id} | vulbox | {project} | {args.title[:30]} | {sev_zh} | 已提交 | （提交后填） |
"""
    return md


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("task_id", help="任务 ID，如 V-001")
    ap.add_argument("title", help="漏洞标题，如 'coloros.com 反射型XSS'")
    ap.add_argument("severity", choices=list(SEVERITY_MAP.keys()))
    ap.add_argument("--vuln-url", help="漏洞域名/URL")
    ap.add_argument("--vuln-type", default="其他", help="漏洞类型（必须是漏洞盒子下拉里的值）")
    ap.add_argument("--desc", help="漏洞简述（多行文本）")
    ap.add_argument("--reproduce-html", help="复现步骤 HTML（富文本）")
    ap.add_argument("--fix-html", help="修复建议 HTML（富文本）")
    ap.add_argument("--evidence-files", help="证据文件路径，逗号分隔")
    ap.add_argument("--output", help="输出 Markdown 路径")
    args = ap.parse_args()

    md = build_report(args)

    out = args.output
    if not out:
        out = f"C:\\Users\\Administrator\\Desktop\\vuln_report_{args.task_id.replace('-','_')}.md"
    Path(out).write_text(md, encoding="utf-8")
    print(f"✅ 报告已生成: {out}")
    print(f"   任务 {args.task_id} → {get_project_info(args.task_id)}")
    print(f"   严重程度: {severity_zh(args.severity)}")
    print(f"\n⚠️  AI 不代提交 — 复制字段值到漏洞盒子表单 → 手动勾协议 → 手动点提交")


if __name__ == "__main__":
    main()