feat(recon): grupo de reconocimiento de red + servicios + fingerprint web

Añade el capability group `recon` (dominio cybersecurity + pipelines, Python),
con la política de archivado OSINT y página madre docs/capabilities/recon.md.

Lookups y sondeo (wrappers de CLI):
- whois_lookup, rdap_lookup, dns_records, ping_host, traceroute_host, nmap_scan
- save_scan_to_osint (sink común) + recon_osint (pipeline one-shot scan+archivado)

Escaneo de puertos/servicios nativo (stdlib, sin nmap ni sudo):
- scan_tcp_ports: connect-scan TCP concurrente (open/closed/filtered)
- grab_service_banner: banner grab + identificación de servicio/versión real
- identify_port_service: puro, puerto -> servicio IANA esperado (~120 puertos)
- scan_port_services: pipeline one-shot (scan -> identify + banner por puerto abierto)

Fingerprint de tecnología web (estilo Wappalyzer), patrón pura/impura:
- fetch_http_fingerprint: GET stdlib, recoge headers/html/cookies (solo nombres)
- detect_web_tech: puro, matchea ~50 firmas regex -> tecnologías por categoría
- fingerprint_web_stack: pipeline one-shot url -> tecnologías

Todas devuelven dict {status} sin lanzar. Tests: 43 verdes, sin red externa.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-14 15:12:07 +02:00
parent d89da1292d
commit 935008ec3f
49 changed files with 6659 additions and 302 deletions
@@ -0,0 +1,64 @@
---
name: identify_port_service
kind: function
lang: py
domain: cybersecurity
version: "1.0.0"
purity: pure
signature: "def identify_port_service(port: int, proto: str = 'tcp') -> dict"
description: "Mapea un número de puerto (TCP/UDP) a su servicio IANA well-known más una descripción corta, usando una tabla estática embebida (~120 puertos comunes en pentest/OSINT). Función pura, sin red ni I/O: dado un puerto abierto detectado por un scanner, dice qué servicio se ESPERA típicamente ahí por convención (ssh en 22, https en 443, mysql en 3306, postgresql en 5432, rdp en 3389, redis en 6379, mongodb en 27017, etc.), no lo verifica en vivo. Complementa a scan_tcp_ports/scan_port_tcp y grab_service_banner para enriquecer informes de reconocimiento de red."
tags: [recon, cybersecurity, port-service, port, service, iana, well-known, ports, network, osint]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: []
tested: true
tests: ["test_port_22_es_ssh", "test_port_443_es_https", "test_port_3306_es_mysql", "test_port_53_udp_es_dns", "test_puerto_fuera_de_rango_es_invalid", "test_puerto_desconocido_known_false", "test_proto_invalido_es_invalid", "test_proto_se_normaliza_mayusculas", "test_determinismo_misma_entrada_misma_salida"]
test_file_path: "python/functions/cybersecurity/identify_port_service_test.py"
file_path: "python/functions/cybersecurity/identify_port_service.py"
params:
- name: port
desc: "Número de puerto a identificar, rango válido 0-65535. Fuera de rango devuelve service 'invalid'."
- name: proto
desc: "Protocolo de transporte: 'tcp' o 'udp' (default 'tcp'). Se normaliza a minúsculas; cualquier otro valor devuelve service 'invalid'."
output: "dict con {port: int, proto: str, service: str, description: str, known: bool}. service='ssh'/'https'/... y known=True si hay match en la tabla; service='unknown', description='', known=False si el puerto/proto es válido pero no está catalogado; service='invalid' si el puerto está fuera de rango 0-65535 o el protocolo no es tcp/udp."
---
## Ejemplo
```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from cybersecurity.identify_port_service import identify_port_service
identify_port_service(22)
# -> {"port": 22, "proto": "tcp", "service": "ssh",
# "description": "Secure Shell", "known": True}
identify_port_service(53, "udp")
# -> {"port": 53, "proto": "udp", "service": "dns",
# "description": "Domain Name System", "known": True}
identify_port_service(99999)
# -> {"port": 99999, "proto": "tcp", "service": "invalid",
# "description": "", "known": False}
```
Invocación directa via `fn run`:
```bash
./fn run identify_port_service_py_cybersecurity
# imprime el JSON de varios puertos de muestra (22, 443, 53/udp, 3306, 99999)
```
## Cuando usarla
Cuando tienes un puerto abierto (por ejemplo de `scan_tcp_ports` / `scan_port_tcp_go_cybersecurity` o de un parseo de salida de `nmap_scan`) y quieres el servicio esperado por convención IANA **sin sondear en vivo** — para etiquetar resultados, generar resúmenes legibles o enriquecer informes de reconocimiento (recon/OSINT) de forma determinista y offline.
## Gotchas
- Devuelve el servicio **convencional** según IANA/nmap-services, **no verifica** que sea el que realmente corre en ese puerto. Un servicio puede escuchar en un puerto no estándar (p. ej. SSH en 2222, o un panel admin en 8443). Para confirmar el servicio real, sondea con `grab_service_banner` / `scan_port_tcp_go_cybersecurity` (que devuelve banner) o `nmap_scan` con detección de versión.
- La tabla cubre los puertos más comunes en pentest/OSINT, no es exhaustiva (no incluye todos los registros IANA). Un puerto válido pero no catalogado devuelve `service: "unknown"`, `known: False` — no es un error.
- `service: "invalid"` (puerto fuera de 0-65535 o proto distinto de tcp/udp) se distingue de `service: "unknown"` (puerto/proto válidos pero no en la tabla). Ambos tienen `known: False`.