--- name: nmap_scan kind: function lang: py domain: cybersecurity version: "1.1.0" purity: impure signature: "def nmap_scan(target: str, profile: str = 'quick', ports: str | None = None, extra_args: list[str] | None = None, out_dir: str | None = None, timeout_s: int = 1800, confirm: bool = False, allowlist: list[str] | None = None) -> dict" description: "Wrapper de `nmap` por perfiles para reconocimiento de red. Ejecuta nmap como subprocess forzando salida XML (-oX), la parsea con ElementTree y devuelve puertos abiertos y hosts vivos de forma estructurada. Funcion estrella de recon: corre en primer plano (quick, top1000, service) y segundo plano para scans largos (full-tcp, vuln, udp-top). NO lanza: devuelve dict status ok/error. Sin sudo por defecto (connect-scan TCP)." tags: [recon, nmap, portscan, cybersecurity] params: - name: target desc: "Host, IP o rango CIDR a escanear (ej. 'scanme.nmap.org', '192.168.1.10', o '192.168.1.0/24' con el perfil discovery). Vacio devuelve status error." - name: profile desc: "Clave de PROFILES que determina los flags de nmap. quick=(-T4 -F) top 100 puertos rapido; top1000=(-T4) los 1000 puertos default; full-tcp=(-p- -T4) los 65535 TCP, LARGO; service=(-sV -sC -T4) deteccion de version + scripts default; udp-top=(-sU --top-ports 100 -T4) UDP top 100, LARGO y suele requerir sudo; vuln=(-sV --script vuln -T4) scripts de vulnerabilidades, LARGO; discovery=(-sn) ping sweep / host discovery de una subred; aggressive=(-A -T4) OS+version+script+traceroute (el -O interno puede pedir sudo); os=(-O) OS detection, REQUIERE sudo/root. Perfil invalido devuelve status error listando los validos." - name: ports desc: "Especificacion de puertos para -p (ej. '22,80,443' o '1-1000'). Si se pasa, anade '-p ' al comando. None deja los puertos que defina el perfil." - name: extra_args desc: "Lista de flags adicionales de nmap a anadir tal cual al comando (ej. ['--open', '-Pn']). None no anade nada." - name: out_dir desc: "Directorio donde guardar el XML. Si se pasa, se crea y el XML se guarda como nmap---.xml (util para scans largos en background y conservar el resultado). None usa un archivo temporal." - name: timeout_s desc: "Segundos maximos de ejecucion del subprocess. Default 1800 (30 min). Para scans largos (full-tcp, vuln, udp-top) subir este valor; superarlo devuelve status error con mensaje claro." - name: confirm desc: "Confirmacion explicita para escanear un target publico o desconocido. Default False: si el target no es claramente privado/local (10.x, 192.168.x, 127.x, localhost, *.local/.lan/.internal/.home/.corp) y no esta en allowlist, el escaneo se rechaza con status error y needs_confirm=True (proteccion anti-escaneo no autorizado). Pasar True solo con autorizacion. No hace DNS lookup (sin red)." - name: allowlist desc: "Lista de targets autorizados. Un target pasa el guard sin confirm si coincide exactamente con una entrada o termina en ella (ej. ['scanme.nmap.org'] o ['example.com']). None o lista vacia no autoriza nada." output: "dict. ok: {status:'ok', target, profile, command (cmd ejecutado), open_ports:[{port:int,proto,state,service,product,version}] (solo open/open|filtered), hosts_up:[ips] (host discovery), host_status, xml_path (siempre presente), raw (stdout de nmap, siempre presente), elapsed_s:float, started (ISO)}. error: {status:'error', error:str}. Si el guard rechaza el target (publico/desconocido sin confirm ni allowlist) el error tambien incluye needs_confirm:True. Nunca lanza excepciones." uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_py_core" imports: [] tested: true tests: - test_parse_xml_extrae_puertos_abiertos_y_hosts_up - test_guard_publico_sin_confirm_rechaza_y_no_ejecuta - test_guard_privado_procede_y_parsea - test_guard_confirm_true_sobre_publico_procede - test_guard_allowlist_procede - test_perfil_invalido_devuelve_error - test_target_vacio_devuelve_error - test_target_is_private_clasifica test_file_path: "python/functions/cybersecurity/nmap_scan_test.py" file_path: "python/functions/cybersecurity/nmap_scan.py" --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from cybersecurity import nmap_scan # 1) Scan rapido en primer plano contra el host oficial de pruebas de nmap # (scanme.nmap.org es legal escanear). res = nmap_scan("scanme.nmap.org", profile="quick", timeout_s=120) if res["status"] == "ok": for p in res["open_ports"]: print(p["port"], p["proto"], p["service"], p["product"], p["version"]) else: print("error:", res["error"]) # 2) Scan LARGO de los 65535 puertos TCP guardando el XML en out_dir. # Lanzar en segundo plano (background) por la duracion; el XML queda en disco. res = nmap_scan( "scanme.nmap.org", profile="full-tcp", out_dir="/tmp/nmap-runs", timeout_s=7200, # 2h: full-tcp puede tardar minutos a horas allowlist=["scanme.nmap.org"], # autorizado -> pasa el guard sin confirm ) print(res["status"], res.get("xml_path")) # 3) Guard de seguridad: un target publico SIN confirm ni allowlist se rechaza. res = nmap_scan("8.8.8.8") # publico, sin confirm print(res["status"], res.get("needs_confirm")) # "error" True # Para escanear un publico autorizado: confirm=True (o anadirlo a allowlist). res = nmap_scan("8.8.8.8", confirm=True) # Un target privado/local NO requiere confirm: res = nmap_scan("192.168.1.10") # procede directamente ``` ## Cuando usarla Usala para el reconocimiento de puertos y servicios de un host: mapear la superficie de ataque antes de un pentest autorizado, descubrir que servicios y versiones expone una IP, o barrer una subred con `profile="discovery"` para ver que hosts estan vivos. Es la funcion estrella de recon del registry. Para scans largos (`full-tcp`, `vuln`, `udp-top`) lanza la llamada en SEGUNDO PLANO: tardan de minutos a horas. Pasa `out_dir` para conservar el XML en disco y sube `timeout_s` (p.ej. 7200) para que no aborte por timeout. ## Gotchas - GUARD anti-escaneo no autorizado: por defecto (`confirm=False`) la funcion RECHAZA con status error + `needs_confirm=True` cualquier target que no sea claramente privado/local (rangos privados, loopback, link-local, `localhost`, `*.local/.lan/.internal/.home/.corp`). Para escanear un target publico o un hostname desconocido tienes que pasar `confirm=True` o incluirlo en `allowlist` (match exacto o por sufijo). El guard NO hace DNS lookup (sin red, KISS): un hostname publico se considera "indecidible" y cae al lado seguro (requiere confirm). Esto NO sustituye tu responsabilidad legal — solo evita disparos accidentales contra infra ajena. - LEGAL: solo escanea hosts que sean tuyos o para los que tengas autorizacion explicita. `scanme.nmap.org` es el host oficial de pruebas de nmap, legal escanear; cualquier otro objetivo de terceros sin permiso puede ser delito. - Privilegios: los perfiles `os` (-O), `udp-top` (-sU) y parte de `aggressive` (-O interno) requieren sudo/root. Sin privilegios nmap cae a connect-scan TCP (-sT) y esos modos fallan o quedan incompletos — esta funcion no usa sudo. - Duracion: `full-tcp` (65535 puertos), `vuln` (scripts NSE) y `udp-top` (UDP es lento) tardan minutos a horas. Sube `timeout_s` y/o lanza en background con `out_dir`; superar `timeout_s` devuelve status error. - Deteccion: firewalls / IDS / WAF pueden detectar y bloquear el escaneo (sobre todo `aggressive`, `vuln` y `-T4`). El resultado puede venir filtrado o incompleto si el objetivo defiende activamente. - `discovery` (-sn) espera notacion de host o subred en CIDR (ej. "192.168.1.0/24"); puebla `hosts_up`, no `open_ports`. - No lanza excepciones: siempre revisa `res["status"]` antes de leer `open_ports`/`hosts_up`. `raw` y `xml_path` solo estan garantizados en ok. ## Capability growth log - v1.1.0 (2026-06-14) — guard `confirm`/`allowlist` anti-escaneo-no-autorizado: targets publicos/desconocidos se rechazan (status error + needs_confirm) salvo confirm=True o estar en allowlist; privados/local proceden sin confirm. Sin DNS lookup. Anadidos tests (8 casos: parseo XML, guard publico/privado/confirm/ allowlist, perfil invalido, target vacio, clasificacion _target_is_private).