feat(recon): modo CDP en fingerprint_web_stack para detectar SPAs
Añade fetch_http_fingerprint_cdp_py_browser (domain browser): recoge el HTML renderizado tras ejecutar JavaScript usando un Chrome remoto via CDP, componiendo cdp_open_url_and_wait + cdp_eval. Devuelve la misma estructura que el fetch estático para que detect_web_tech lo consuma sin cambios. Integra use_cdp en el pipeline fingerprint_web_stack (v1.1.0): combina los headers reales del fetch estático con el HTML post-JS del CDP. Detecta frameworks de SPA (React/Vue/Angular/Next) que el fetch estático no ve porque montan el DOM en runtime. Si no hay Chrome en cdp_port, degrada al fetch estático con un warning (no rompe). cdp_port=9333 (Chrome aislado) recomendado para terceros, 9222 diario. Verificado en vivo (Chrome 9333): sobre una SPA cuyo marcador de framework solo aparece tras ejecutar JS, el estático detecta solo nginx; con use_cdp=True detecta además Next.js, React y Node.js. Tests: 48 verdes (error path sin Chrome + happy path mockeado + degradación). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: fetch_http_fingerprint_cdp
|
||||
kind: function
|
||||
lang: py
|
||||
domain: browser
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def fetch_http_fingerprint_cdp(url: str, *, port: int = 9222, wait_render_s: float = 2.0, timeout_s: float = 30.0, close_tab: bool = True) -> dict"
|
||||
description: "Fingerprint web con HTML RENDERIZADO tras ejecutar JavaScript via Chrome DevTools Protocol (CDP). Navega con un Chrome remoto, espera a que la SPA monte el DOM y recoge el HTML post-JS, titulo, URL final y nombres de cookie. Detecta frameworks que el fetch estatico NO ve: React, Vue, Angular, Next, Svelte montados en runtime. Wappalyzer dinamico: devuelve la MISMA estructura que fetch_http_fingerprint para que detect_web_tech la consuma sin cambios. Recon web de SPAs / single-page applications con HTML inicial vacio."
|
||||
tags: [recon, web-recon, browser, cdp, fingerprint, spa, wappalyzer, javascript, react, vue, angular]
|
||||
uses_functions: ["cdp_open_url_and_wait_py_pipelines", "cdp_eval_py_browser"]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_py_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: url
|
||||
desc: "URL objetivo del fingerprint (sitio a inspeccionar)."
|
||||
- name: port
|
||||
desc: "Puerto de remote debugging del Chrome a usar. Default 9222 (navegador diario, activado global). Para aislamiento de recon de terceros, apuntar a 9333 (Chrome aislado del browser_mcp)."
|
||||
- name: wait_render_s
|
||||
desc: "Segundos extra de espera tras el load event para que el JS de la SPA pinte el DOM (el load NO garantiza render completo). Default 2.0."
|
||||
- name: timeout_s
|
||||
desc: "Timeout de la navegacion en segundos. Default 30.0."
|
||||
- name: close_tab
|
||||
desc: "Si True, cierra el tab al terminar (best-effort via window.close()) para no dejar pestanas abiertas. Default True."
|
||||
output: "dict siempre (nunca lanza). En exito: {status:'ok', url, final_url, title, status_code:None, headers:{}, cookies:[solo nombres no-httponly], html:<RENDERIZADO post-JS>, html_len, rendered:True, raw}. En error: {status:'error', error:<mensaje claro>, url}. status_code/headers quedan vacios porque CDP no expone la capa de red; esta funcion aporta el HTML renderizado, que es lo que detect_web_tech necesita para una SPA."
|
||||
tested: true
|
||||
tests: ["test_sin_chrome_devuelve_error_sin_lanzar", "test_url_vacia_devuelve_error", "test_happy_path_monkeypatch", "test_happy_path_eval_falla_devuelve_error"]
|
||||
test_file_path: "python/functions/browser/fetch_http_fingerprint_cdp_test.py"
|
||||
file_path: "python/functions/browser/fetch_http_fingerprint_cdp.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os, json
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
from browser.fetch_http_fingerprint_cdp import fetch_http_fingerprint_cdp
|
||||
from cybersecurity.detect_web_tech import detect_web_tech
|
||||
|
||||
# Recoge el HTML RENDERIZADO (post-JS) de una SPA via el Chrome diario (9222).
|
||||
res = fetch_http_fingerprint_cdp("https://react.dev/", port=9222)
|
||||
if res["status"] == "ok":
|
||||
# detect_web_tech (PURA) consume las mismas senales que fetch_http_fingerprint.
|
||||
tech = detect_web_tech(
|
||||
res["headers"], # {} con CDP — usa el fetch estatico para headers
|
||||
html=res["html"], # el HTML RENDERIZADO post-JS: aqui esta la clave
|
||||
cookies=res["cookies"], # solo nombres
|
||||
final_url=res["final_url"],
|
||||
)
|
||||
print(json.dumps(tech, ensure_ascii=False, indent=2))
|
||||
else:
|
||||
print("error:", res["error"])
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando el fetch estatico (`fetch_http_fingerprint`) NO detecta el framework porque
|
||||
el sitio es una SPA que monta el DOM con JavaScript (HTML inicial casi vacio:
|
||||
`<div id="root">` o `<div id="__next">` sin contenido). Esta funcion recoge el HTML
|
||||
DESPUES de que el JS pinte, de modo que `detect_web_tech` ve React / Vue / Angular /
|
||||
Next igual que un Wappalyzer dinamico. Requiere un Chrome con remote debugging.
|
||||
Combina ambas capas para fingerprint completo: estatico para headers + status +
|
||||
cookies httponly; CDP para el HTML renderizado.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Requiere un Chrome con remote debugging** escuchando en `port`: 9222 (navegador
|
||||
diario, ya activado global) o 9333 (Chrome aislado del browser_mcp). Sin Chrome
|
||||
vivo devuelve `{status:"error", error:"no hay Chrome en el puerto N (¿remote debugging activo?)"}` — no lanza.
|
||||
- **Abre un tab en ESE navegador.** Con `port=9222` mezcla la sesion de tu navegador
|
||||
PERSONAL (cookies de tu sesion, historial). Para recon de TERCEROS prefiere
|
||||
`port=9333` (aislado) para no contaminar ni filtrar tu sesion.
|
||||
- **`document.cookie` NO ve cookies httponly** (las de sesion casi siempre lo son):
|
||||
esas y los headers de respuesta vienen mejor del fetch estatico `fetch_http_fingerprint`.
|
||||
- **`headers` y `status_code` quedan vacios/None**: CDP no expone la capa de red sin
|
||||
el dominio Network. Esta funcion aporta el HTML renderizado, no la red. Si necesitas
|
||||
el status real o headers, usa el fetch estatico en paralelo.
|
||||
- **`wait_render_s` puede ser insuficiente** para SPAs lentas (mucho data-fetching tras
|
||||
el load). Si el `html` sale incompleto, sube `wait_render_s` (ej. 4.0-6.0).
|
||||
- **Respeta scope y autorizacion legal**: solo inspecciona sitios que tengas permiso
|
||||
para analizar.
|
||||
Reference in New Issue
Block a user