#!/usr/bin/env python3
"""Authorized Nmap automation wrapper.

Run only against assets explicitly listed in a signed scope of work. The wrapper
requires a scope file and defaults to conservative discovery and service-version
checks. It does not include exploit scripts or stealth options.
"""

from __future__ import annotations

import argparse
import ipaddress
import json
import subprocess
from datetime import datetime, timezone
from pathlib import Path


def load_scope(scope_file: Path) -> list[str]:
    data = json.loads(scope_file.read_text(encoding="utf-8"))
    targets = data.get("authorized_targets", [])
    if not targets:
        raise SystemExit("scope file must contain authorized_targets")
    for target in targets:
        try:
            ipaddress.ip_network(target, strict=False)
        except ValueError:
            if not all(part and part.replace("-", "").isalnum() for part in target.split(".")):
                raise SystemExit(f"invalid target in scope: {target}")
    return targets


def run_nmap(targets: list[str], output_dir: Path) -> Path:
    output_dir.mkdir(parents=True, exist_ok=True)
    stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    xml_base = output_dir / f"nmap_authorized_{stamp}"
    command = [
        "nmap",
        "-sV",
        "--version-light",
        "--reason",
        "--open",
        "-oA",
        str(xml_base),
        *targets,
    ]
    print("Running:", " ".join(command))
    subprocess.run(command, check=True)
    return xml_base.with_suffix(".xml")


def main() -> int:
    parser = argparse.ArgumentParser(description="Run scoped Nmap reconnaissance for authorized tests.")
    parser.add_argument("--scope", type=Path, required=True, help="JSON file with authorized_targets array")
    parser.add_argument("--output-dir", type=Path, default=Path("recon_output"))
    args = parser.parse_args()
    targets = load_scope(args.scope)
    output = run_nmap(targets, args.output_dir)
    print(f"Nmap XML written to {output}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
