Legal Cookies
Metodologia

Motor de Analisis

Documentacion tecnica del motor de analisis basado en Puppeteer, Chrome DevTools Protocol y contextos incognito.

El motor de analisis de Legal Cookies se basa en Puppeteer con Chromium headless, complementado con Chrome DevTools Protocol (CDP) para capacidades avanzadas de interceptacion.

Puppeteer y Chromium

Puppeteer es una biblioteca de Node.js que proporciona una API de alto nivel para controlar navegadores Chromium. Lo utilizamos porque:

  • Fidelidad total: Ejecuta el mismo motor de renderizado que Chrome, garantizando que vemos exactamente lo que veria un usuario
  • Control completo: Acceso a todas las APIs del navegador (cookies, storage, red)
  • CDP integrado: Capacidad de usar el protocolo de DevTools para funciones avanzadas
  • Headless: Funciona sin interfaz grafica, ideal para automatizacion

Configuracion del navegador

const browser = await puppeteer.launch({
  headless: true,
  args: [
    '--no-sandbox',
    '--disable-setuid-sandbox',
    '--disable-dev-shm-usage',
    '--disable-gpu'
  ]
});

Viewport y User-Agent

Simulamos un navegador de escritorio estandar:

ConfiguracionValor
Viewport1280x800
User-AgentChrome moderno en Windows/Mac
Timeout navegacion30 segundos
Timeout por defecto10 segundos

Contextos Incognito

Cada analisis se ejecuta en un BrowserContext independiente, equivalente a una ventana de incognito:

// Crear contexto aislado
const context = await browser.createBrowserContext();

// Crear pagina en el contexto
const page = await context.newPage();

// ... realizar analisis ...

// Cerrar contexto (limpia automaticamente todo)
await context.close();

Beneficios del contexto incognito

BeneficioDescripcion
AislamientoCada analisis tiene su propio almacenamiento
Limpieza automaticaAl cerrar, se eliminan cookies, cache y storage
Sin temporales huerfanosNo se acumulan perfiles de navegador en disco
ReproducibilidadCada analisis parte del mismo estado inicial

Antes de implementar contextos incognito, los temporales de Puppeteer podian acumular gigabytes de datos en /tmp. Ahora cada analisis se limpia automaticamente al terminar.

Chrome DevTools Protocol (CDP)

CDP es un protocolo de bajo nivel que permite comunicacion directa con el motor de Chromium. Lo utilizamos para detectar cookies HttpOnly que no son visibles a JavaScript.

El problema de las cookies HttpOnly

Las cookies marcadas como HttpOnly son una medida de seguridad que impide que JavaScript acceda a ellas (proteccion contra XSS). Sin embargo, esto tambien significa que la API estandar de Puppeteer para obtener cookies no las detecta.

Solucion: Interceptar headers HTTP

Activamos una sesion CDP para interceptar todos los headers Set-Cookie de las respuestas HTTP:

// Crear sesion CDP
const cdpSession = await page.createCDPSession();

// Habilitar eventos de red
await cdpSession.send('Network.enable');

// Escuchar respuestas HTTP
cdpSession.on('Network.responseReceived', (params) => {
  const setCookieHeaders = params.response.headers['set-cookie'];
  if (setCookieHeaders) {
    // Parsear y almacenar cookies HttpOnly
    parseCookiesFromHeaders(setCookieHeaders);
  }
});

Los headers Set-Cookie tienen un formato especifico que parseamos:

Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Lax

Extraemos:

  • Nombre y valor: session_id=abc123
  • Atributos: Path, HttpOnly, Secure, SameSite, Expires, Max-Age
  • Dominio: Del header o inferido de la URL

Merge de cookies

El resultado final combina ambas fuentes, eliminando duplicados:

const jsCookies = await extractCookies(page);        // Accesibles a JS
const httpOnlyCookies = extractFromHeaders(headers); // HttpOnly via CDP

// Merge por (name + domain + path)
const allCookies = mergeCookies(jsCookies, httpOnlyCookies);

Cada cookie incluye un campo source que indica su origen:

sourceDescripcion
javascriptDetectada via API de Puppeteer
http-headerDetectada via header Set-Cookie

Interceptor de Requests

Capturamos todas las peticiones de red para detectar requests a terceros:

await page.setRequestInterception(true);

page.on('request', (request) => {
  const url = new URL(request.url());

  // Detectar si es third-party
  if (url.hostname !== targetDomain) {
    thirdPartyRequests.push({
      url: request.url(),
      domain: url.hostname,
      type: request.resourceType()
    });
  }

  request.continue();
});

Tipos de recursos capturados

TipoIncluidoRazon
scriptSiScripts de tracking/analytics
imageSiPixeles de tracking
xhr/fetchSiBeacons y APIs de terceros
iframeSiEmbeds de terceros
stylesheetNoGeneralmente no implica tracking
fontNoNo implica tratamiento de datos

Captura post-load (10 segundos)

Muchos scripts de tracking se cargan de forma diferida para no afectar al tiempo de carga percibido. Por eso mantenemos el interceptor activo 10 segundos adicionales despues de que la pagina alcance networkidle2:

// Navegar y esperar carga inicial
await page.goto(url, { waitUntil: 'networkidle2' });

// Esperar 10 segundos adicionales para scripts diferidos
await new Promise(resolve => setTimeout(resolve, 10000));

// Ahora si obtener los requests capturados
const requests = getInterceptedRequests();

Esta espera de 10 segundos es un balance entre exhaustividad y tiempo de analisis. Scripts que se cargan mas tarde (por ejemplo, tras scroll o interaccion) no seran detectados.

Deteccion de CMPs

Detectamos plataformas de gestion de consentimiento (CMP) con un sistema hibrido de 3 capas:

Capa 1: CMPs conocidos por firma DOM

Buscamos selectores especificos de 25+ CMPs conocidos:

const KNOWN_CMPS = [
  { name: 'OneTrust', selector: '#onetrust-consent-sdk' },
  { name: 'Cookiebot', selector: '#CybotCookiebotDialog' },
  { name: 'Didomi', selector: '#didomi-host' },
  { name: 'TrustArc', selector: '#truste-consent-track' },
  // ... 21 mas
];

Capa 2: IAB TCF v2.0 API

Si no encontramos un CMP conocido, verificamos si existe la API estandar de la industria:

// Verificar existencia de __tcfapi (IAB TCF v2.0)
const tcfData = await page.evaluate(() => {
  if (typeof window.__tcfapi === 'function') {
    return { exists: true };
  }
  return null;
});

Capa 3: Heuristica visual

Como ultimo recurso, buscamos banners con palabras clave tipicas de interfaces de consentimiento:

  • cookie, consent, privacy, gdpr
  • privacidad, aceptar, rechazar
  • preferencias, configuracion

Resultado de deteccion

El resultado indica el metodo utilizado:

interface CMPDetection {
  name: string | null;
  detected: boolean;
  bannerVisible: boolean;
  configured: boolean;
  method?: 'known' | 'tcf_api' | 'heuristic';
  tcfCmpId?: number;
}

Verificacion de funcionamiento del CMP

Una vez detectado un CMP, intentamos rechazar el consentimiento para verificar si realmente bloquea cookies y scripts:

Proceso de verificacion

  1. Captura inicial: Registramos todas las cookies presentes antes de interactuar
  2. Busqueda de boton de rechazo: Usamos selectores especificos para cada CMP conocido
  3. Click en rechazo: Hacemos click en el boton de "Rechazar" o "Solo necesarias"
  4. Espera de procesamiento: 2 segundos para que el CMP procese la decision
  5. Captura final: Registramos las cookies despues del rechazo
  6. Comparacion: Calculamos que cookies fueron bloqueadas

Selectores de rechazo por CMP

Cada CMP tiene selectores especificos para su boton de rechazo:

const CMP_REJECT_SELECTORS = {
  "OneTrust": ["#onetrust-reject-all-handler", ...],
  "Cookiebot": ["#CybotCookiebotDialogBodyButtonDecline", ...],
  "Didomi": ["#didomi-notice-disagree-button", ...],
  "CookieYes": [".cky-btn-reject", ...],
  // ... 25+ CMPs soportados
};

Resultado del analisis de consentimiento

El informe incluye:

interface ConsentAnalysis {
  status: "cmp_rejected" | "cmp_not_interactable" | "no_cmp";
  cookiesBeforeReject: number;    // Cookies antes de rechazar
  cookiesAfterReject: number;     // Cookies despues de rechazar
  cookiesBlocked: RawCookie[];    // Cookies que fueron eliminadas
  cookiesStillPresent: RawCookie[]; // Cookies que persisten
  rejectMethod: string;           // Selector usado para rechazar
}

Interpretacion

ResultadoSignificado
cmp_rejected + cookiesBlocked > 0CMP funciona: elimino cookies al rechazar
cmp_rejected + cookiesBlocked = 0CMP detectado pero no elimino cookies
cmp_not_interactableCMP detectado pero no pudimos interactuar
no_cmpNo se detecto plataforma de consentimiento

Si el CMP es detectado pero no podemos interactuar (boton no encontrado, timeout, etc.), lo registramos como cmp_not_interactable con una nota explicativa.

Limpieza de temporales

Historicamente, Puppeteer creaba directorios temporales en /tmp/puppeteer_* que no se limpiaban automaticamente. Con nuestro sistema de contextos incognito, esto ya no es un problema.

Adicionalmente, proporcionamos un script de limpieza para eliminar temporales huerfanos existentes:

npx tsx scripts/cleanup-chromium-temps.ts

Este script busca y elimina directorios que coincidan con los patrones:

  • puppeteer_dev_chrome_profile-*
  • puppeteer_dev_profile-*
  • chromium-*
  • .org.chromium.*