935008ec3f
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>
6.2 KiB
6.2 KiB
name, kind, lang, domain, version, purity, signature, description, tags, params, output, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | params | output | uses_functions | uses_types | returns | returns_optional | error_type | imports | tested | tests | test_file_path | file_path | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| detect_web_tech | function | py | cybersecurity | 1.0.0 | pure | def detect_web_tech(headers: dict, html: str = '', cookies: list[str] | None = None, final_url: str = '') -> dict | Detector de tecnologia web estilo Wappalyzer: identifica el stack tecnologico de un sitio (web fingerprint) matcheando una tabla de firmas regex embebida contra las cabeceras HTTP, el HTML, los nombres de cookies y la URL final. Detecta servidor (nginx, Apache, IIS, LiteSpeed, Caddy), lenguaje (PHP, ASP.NET, Java, Python, Ruby, Node.js), CMS (WordPress, Drupal, Joomla, Shopify, Wix, Squarespace, Ghost), frameworks JS (React, Vue, Angular, Svelte, Next.js, Nuxt), librerias (jQuery, Bootstrap, Lodash, Modernizr), analytics/tag (Google Analytics, GTM, Facebook Pixel, Hotjar, Matomo), CDN (Cloudflare, Fastly, Akamai, CloudFront, jsDelivr, unpkg), ecommerce (WooCommerce, Magento, PrestaShop, Shopify) y WAF/seguridad (Cloudflare, Sucuri, Imperva Incapsula). Pieza pura del detector: no toca la red, recibe las senales ya recogidas por fetch_http_fingerprint. |
|
|
dict con technologies (lista de {name, category, version, confidence, evidence} ordenada deterministicamente por categoria y nombre), by_category (dict categoria -> lista de nombres) y count (entero). confidence es 'high' para match directo de header/meta/cookie/url y 'medium' para HTML generico, script src o tecnologia implicada. version es best-effort (a menudo ''). Para entrada vacia devuelve technologies [], by_category {}, count 0. Nunca lanza. | false | true |
|
python/functions/cybersecurity/detect_web_tech_test.py | python/functions/cybersecurity/detect_web_tech.py |
Ejemplo
from cybersecurity import detect_web_tech
# Senales fake de un sitio WordPress sobre nginx.
headers = {
"server": "nginx/1.24.0",
"x-powered-by": "PHP/8.2",
}
html = (
'<html><head>'
'<meta name="generator" content="WordPress 6.4">'
'</head><body><link href="/wp-content/themes/x/style.css"></body></html>'
)
cookies = ["PHPSESSID", "wordpress_logged_in_abc"]
result = detect_web_tech(headers, html=html, cookies=cookies)
# result["count"] == 3
# result["by_category"] == {
# "cms": ["WordPress"],
# "programming-language": ["PHP"],
# "web-server": ["nginx"],
# }
# nginx -> version "1.24.0", confidence "high", evidence "header server: nginx/1.24.0"
# WordPress -> version "6.4", confidence "high", evidence "meta generator: WordPress 6.4"
# PHP -> version "8.2", confidence "high", evidence "header x-powered-by: PHP/8.2"
Flujo real componiendo con la capa impura hermana (recoleccion -> matching):
from cybersecurity import fetch_http_fingerprint, detect_web_tech
# Capa impura: recoge las senales con un GET real (red).
fp = fetch_http_fingerprint("https://example.com")
# fp = {"headers": {...lowercase...}, "html": "...", "cookies": [...], "final_url": "..."}
# Capa pura: identifica el stack sobre las senales recogidas (sin red).
tech = detect_web_tech(
fp["headers"],
html=fp.get("html", ""),
cookies=fp.get("cookies"),
final_url=fp.get("final_url", ""),
)
for t in tech["technologies"]:
print(t["category"], t["name"], t["version"], t["confidence"])
Cuando usarla
Cuando ya tienes los headers + html (+ cookies/URL) de una URL — recogidos por
fetch_http_fingerprint_py_cybersecurity — y quieres saber el stack tecnologico
del sitio: servidor, lenguaje, CMS, frameworks JS, librerias, analytics, CDN,
ecommerce y WAF. Usala como pieza de matching pura y testeable. Para el flujo
one-shot url -> tecnologias (recoger + detectar en una llamada) usa el pipeline
fingerprint_web_stack.
Gotchas
- La tabla
SIGNATURESes un subconjunto curado de lo que cubre Wappalyzer (~50 tecnologias), no es exhaustiva. Para ampliarla, anade entradas nuevas a la constanteSIGNATURESdel modulo siguiendo el formato documentado en el codigo (matchersheaders/html/meta_generator/cookies/script_src/url, opcionalesversion_groupeimplies). - La deteccion por HTML generico puede dar falsos positivos: un sitio que
mencione "wordpress" o "woocommerce" en su texto/blog puede matchear sin usarlo
realmente. Por eso esos matches tienen
confidence: "medium"mientras que header/meta/cookie directos son"high". - Las SPAs cargan los frameworks por JS en runtime. Un fetch estatico (sin
ejecutar JavaScript) ve el HTML inicial, que en muchas SPAs esta casi vacio
(
<div id="root"></div>). React/Vue/Angular pueden NO detectarse si el HTML servido no contiene aun sus marcadores. Para esos casos hace falta renderizar con un navegador headless, fuera del alcance de esta funcion pura. - Las versiones son best-effort: solo se extraen cuando el regex que disparo
tiene un group de version y este matcheo. A menudo quedan en
"". - Es PURA y determinista: misma entrada -> misma salida. Para entrada vacia
(
headers={}, html="") devuelvetechnologies: [], count: 0y NUNCA lanza ni reporta status/error.