# nmap_report.py - doc file ket qua cua nmap (-oX) va viet thanh bao cao co xep muc rui ro.
# Dung thu vien co san cua Python 3. Khong quet gi ca: chi DOC file ban da luu.
# Cach dung:
#   nmap -sT --top-ports 100 --open -oX quet.xml 192.168.1.0/24
#   python3 nmap_report.py quet.xml
#   python3 nmap_report.py quet.xml --so thiet-bi.csv     (so thiet bi: mac,ip,ten,chu)
import argparse
import csv
import xml.etree.ElementTree as ET

# port -> (muc, ly do ngan, viec nen lam)
RISK = {
    21: ("CAO", "FTP: gui mat khau khong ma hoa", "Tat neu khong dung; dung SFTP"),
    23: ("CAO", "Telnet: dieu khien tu xa khong ma hoa", "Tat ngay; dung SSH"),
    3389: ("CAO", "Remote Desktop: hay bi do mat khau", "Tat neu khong can; bat xac thuc 2 lop"),
    5900: ("CAO", "VNC: dieu khien man hinh tu xa", "Tat hoac dat mat khau manh"),
    445: ("CAO", "Chia se file Windows (SMB)", "Chi bat khi can; cap nhat Windows"),
    135: ("TRUNG BINH", "Windows RPC: dich vu he thong", "Binh thuong tren Windows; khong mo ra ngoai"),
    139: ("TRUNG BINH", "NetBIOS: chia se file doi cu", "Tat neu khong dung"),
    22: ("TRUNG BINH", "SSH: quan tri tu xa co ma hoa", "Dung mat khau manh hoac khoa SSH"),
    80: ("TRUNG BINH", "Web khong ma hoa", "Doi mat khau quan tri; uu tien 443"),
    8080: ("TRUNG BINH", "Web/proxy khong ma hoa", "Kiem tra ai dang chay dich vu nay"),
    8000: ("TRUNG BINH", "May chu web thu nghiem", "Tat khi khong con dung"),
    554: ("TRUNG BINH", "Truyen video camera (RTSP)", "Dat mat khau camera; dua vao mang rieng"),
    5000: ("THAP", "AirPlay / dich vu web nho", "Tat AirPlay Receiver neu khong dung"),
    7000: ("THAP", "AirPlay", "Tat AirPlay Receiver neu khong dung"),
    53: ("THAP", "DNS: router tra loi ten mien", "Binh thuong tren router"),
    443: ("THAP", "Web co ma hoa", "Binh thuong"),
    631: ("THAP", "In ma (IPP)", "Binh thuong tren may in"),
    9100: ("THAP", "In truc tiep", "Binh thuong tren may in"),
}
ORDER = {"CAO": 0, "TRUNG BINH": 1, "THAP": 2, "CHUA RO": 3}


def risk_of(port):
    return RISK.get(port, ("CHUA RO", "Port chua co trong bang", "Tra cuu dich vu nay la gi"))


def read_hosts(path):
    hosts = []
    for h in ET.parse(path).getroot().findall("host"):
        addrs = {a.get("addrtype"): a.get("addr") for a in h.findall("address")}
        vendor = next((a.get("vendor") for a in h.findall("address") if a.get("addrtype") == "mac"), "")
        ports = []
        for p in h.findall("ports/port"):
            if p.find("state").get("state") == "open":
                svc = p.find("service")
                ports.append((int(p.get("portid")), svc.get("name") if svc is not None else "?"))
        hosts.append({"ip": addrs.get("ipv4", "?"), "mac": (addrs.get("mac") or "").lower(),
                      "vendor": vendor or "", "ports": sorted(ports)})
    return hosts


def read_book(path):
    # so thiet bi: moi dong la mot thiet bi ban da biet va dong y cho o trong mang
    with open(path, newline="", encoding="utf-8") as f:
        return list(csv.DictReader(f))


def in_book(host, book):
    for row in book:
        mac = (row.get("mac") or "").strip().lower()
        if host["mac"] and mac and mac == host["mac"]:
            return row
        if not host["mac"] and (row.get("ip") or "").strip() == host["ip"]:
            return row
    return None


def main():
    ap = argparse.ArgumentParser(description="Bao cao rui ro tu file nmap -oX")
    ap.add_argument("xml", help="file ket qua nmap luu bang -oX")
    ap.add_argument("--so", help="file CSV so thiet bi (cot: mac,ip,ten,chu) de phat hien thiet bi la")
    args = ap.parse_args()

    hosts = read_hosts(args.xml)
    book = read_book(args.so) if args.so else None
    print(f"Doc {len(hosts)} thiet bi tu {args.xml}\n")
    worst_all = []
    for h in hosts:
        known = in_book(h, book) if book is not None else None
        label = known.get("ten") if known else ("CHUA CO TRONG SO!" if book is not None else "")
        print(f"{h['ip']:<15} {h['vendor'] or h['mac'] or '':<24} {label}")
        if not h["ports"]:
            print("    khong thay port mo (trong pham vi da quet)")
        for port, name in sorted(h["ports"], key=lambda p: ORDER[risk_of(p[0])[0]]):
            level, why, todo = risk_of(port)
            print(f"    [{level:<10}] {port:<6}{name:<12}{why}. -> {todo}")
            worst_all.append((ORDER[level], h["ip"], port, why, todo))
    if book is not None:
        unknown = [h["ip"] for h in hosts if not in_book(h, book)]
        print(f"\nThiet bi chua co trong so: {unknown or 'khong co'}")
    high = [w for w in worst_all if w[0] == 0]
    print(f"\nTong ket: {len(hosts)} thiet bi, {len(worst_all)} port mo, {len(high)} port muc CAO can xu ly truoc.")
    for _, ip, port, why, todo in high:
        print(f"  - {ip}:{port}  {why} -> {todo}")


if __name__ == "__main__":
    main()
