@solvenza/cookies-compliance 1.5.1 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -276,7 +276,7 @@ const config = new ConsentConfigBuilder("1.0.0")
276
276
 
277
277
  ## Purga Automática de Web Storage (localStorage, sessionStorage y Cookies)
278
278
 
279
- A partir de la versión **1.5.1**, cuando el usuario desmarca una categoría en el panel de preferencias o revoca su elección (`Consent.withdraw()`), el SDK no solo elimina las cookies en `document.cookie`, sino que también **purga de forma reactiva las claves guardadas en `localStorage` y `sessionStorage`** por bibliotecas de analítica o marketing (Google Analytics 4, PostHog, Mixpanel, Hotjar, etc.).
279
+ A partir de la versión **1.5.3**, cuando el usuario desmarca una categoría en el panel de preferencias o revoca su elección (`Consent.withdraw()`), el SDK no solo elimina las cookies en `document.cookie`, sino que también **purga de forma reactiva las claves guardadas en `localStorage` y `sessionStorage`** por bibliotecas de analítica o marketing (Google Analytics 4, PostHog, Mixpanel, Hotjar, etc.).
280
280
 
281
281
  ### Soporte de Comodines (Glob Wildcards)
282
282
 
@@ -874,7 +874,7 @@ function enqueue_solvenza_cookies() {
874
874
  "solvenza-cookies",
875
875
  get_template_directory_uri() . "/vendor/consent.min.js",
876
876
  array(),
877
- "1.5.1",
877
+ "1.5.3",
878
878
  false // En <head> para cumplir LSSI antes de scripts de analítica
879
879
  );
880
880
  // Asocia la configuración JSON con el badge flotante habilitado
package/dist/audit.cjs CHANGED
@@ -690,7 +690,7 @@ var LssiAuditor = class {
690
690
  */
691
691
  static async auditLiveUrl(url, findings) {
692
692
  try {
693
- const res = await fetch(url, { headers: { "User-Agent": "Solvenza-LSSI-Auditor/1.5.1" } });
693
+ const res = await fetch(url, { headers: { "User-Agent": "Solvenza-LSSI-Auditor/1.5.3" } });
694
694
  if (!res.ok) {
695
695
  findings.push({
696
696
  id: "URL_FETCH_FAILED",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/audit/index.ts","../src/audit/auditor.ts","../src/presets/presets-catalog.ts","../src/presets/index.ts"],"sourcesContent":["export { LssiAuditor } from \"./auditor.js\";\nexport type {\n AuditReport,\n AuditFinding,\n AuditOptions,\n AuditSeverity,\n} from \"./types.js\";\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ConsentConfig } from \"../core/types.js\";\nimport { resolveConfigPresets } from \"../presets/index.js\";\nimport type { AuditFinding, AuditOptions, AuditReport } from \"./types.js\";\n\n/**\n * Common third-party tracking script domains that require explicit prior consent under LSSI art. 22.2.\n */\nconst KNOWN_TRACKER_PATTERNS = [\n { domain: \"googletagmanager.com/gtm.js\", name: \"Google Tag Manager\", category: \"necessary_or_analytics\" },\n { domain: \"googletagmanager.com/gtag\", name: \"Google Analytics / Ads Gtag\", category: \"analytics\" },\n { domain: \"google-analytics.com/analytics.js\", name: \"Google Universal Analytics\", category: \"analytics\" },\n { domain: \"connect.facebook.net\", name: \"Meta / Facebook Pixel\", category: \"marketing\" },\n { domain: \"static.hotjar.com\", name: \"Hotjar Heatmaps & Feedback\", category: \"analytics\" },\n { domain: \"clarity.ms/tag\", name: \"Microsoft Clarity\", category: \"analytics\" },\n { domain: \"analytics.tiktok.com\", name: \"TikTok Pixel\", category: \"marketing\" },\n { domain: \"snap.licdn.com/insight\", name: \"LinkedIn Insight Tag\", category: \"marketing\" },\n { domain: \"cdn.segment.com/analytics\", name: \"Segment Analytics\", category: \"analytics\" },\n { domain: \"app.posthog.com/static\", name: \"PostHog Analytics\", category: \"analytics\" },\n { domain: \"mc.yandex.ru/metrika\", name: \"Yandex Metrika\", category: \"analytics\" },\n];\n\nexport class LssiAuditor {\n /**\n * Run complete compliance audit on configuration, source code, and/or live URL.\n */\n static async audit(options: AuditOptions): Promise<AuditReport> {\n const findings: AuditFinding[] = [];\n let scannedFilesCount = 0;\n\n // 1. Audit Configuration\n let resolvedConfig: ConsentConfig | null = null;\n if (options.config) {\n if (typeof options.config === \"string\") {\n try {\n const raw = fs.readFileSync(path.resolve(options.config), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración: ${err.message}`,\n remediation: \"Verifica que la ruta al archivo consent.json sea válida y contenga JSON bien formado.\",\n });\n }\n } else {\n resolvedConfig = resolveConfigPresets(options.config);\n }\n } else if (options.configPath) {\n try {\n const raw = fs.readFileSync(path.resolve(options.configPath), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración en '${options.configPath}': ${err.message}`,\n remediation: \"Especifica una ruta válida con --config <ruta>.\",\n });\n }\n }\n\n if (resolvedConfig) {\n this.auditConfig(resolvedConfig, findings);\n }\n\n // 2. Audit Source Code Directory\n if (options.srcDir && fs.existsSync(path.resolve(options.srcDir))) {\n scannedFilesCount = this.auditSourceDirectory(path.resolve(options.srcDir), findings);\n }\n\n // 3. Audit Live URL if provided\n if (options.url) {\n await this.auditLiveUrl(options.url, findings);\n }\n\n // Calculate score and summary\n const criticalCount = findings.filter((f) => f.severity === \"CRITICAL\").length;\n const warningCount = findings.filter((f) => f.severity === \"WARNING\").length;\n const infoCount = findings.filter((f) => f.severity === \"INFO\").length;\n\n const penalty = criticalCount * 25 + warningCount * 10 + infoCount * 2;\n const score = Math.max(0, 100 - penalty);\n const compliant = criticalCount === 0;\n\n return {\n timestamp: new Date().toISOString(),\n compliant,\n score,\n summary: {\n critical: criticalCount,\n warnings: warningCount,\n info: infoCount,\n totalPassed: Math.max(0, 10 - criticalCount - warningCount),\n },\n findings,\n scannedFilesCount,\n };\n }\n\n /**\n * Validate consent configuration against LSSI art. 22.2 and AEPD guidelines.\n */\n static auditConfig(config: ConsentConfig, findings: AuditFinding[]): void {\n if (!config.categories || typeof config.categories !== \"object\") {\n findings.push({\n id: \"CONFIG_NO_CATEGORIES\",\n rule: \"AEPD-CATEGORIES-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"No se encontraron categorías de consentimiento definidas.\",\n remediation: \"Declara al menos la categoría 'necessary' y las categorías opcionales ('analytics', 'marketing').\",\n });\n return;\n }\n\n // Necessary category check\n const nec = config.categories[\"necessary\"];\n if (!nec) {\n findings.push({\n id: \"CONFIG_MISSING_NECESSARY\",\n rule: \"LSSI-NECESSARY-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"Falta la categoría obligatoria 'necessary' (cookies técnicas y de seguridad).\",\n remediation: \"Añade 'necessary: { required: true, label: \\\"Necesarias\\\", description: \\\"...\\\" }'.\",\n });\n } else if (nec.required !== true) {\n findings.push({\n id: \"CONFIG_NECESSARY_NOT_REQUIRED\",\n rule: \"LSSI-NECESSARY-002\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"La categoría 'necessary' debe tener siempre 'required: true'.\",\n remediation: \"Establece required: true en la categoría necessary.\",\n });\n }\n\n // AEPD Legal Guardrail: NO pre-checked optional boxes allowed\n for (const [catId, cat] of Object.entries(config.categories)) {\n if (catId !== \"necessary\" && !cat.required) {\n if (cat.default === true) {\n findings.push({\n id: \"CONFIG_PRECHECKED_OPTIONAL\",\n rule: \"AEPD-PRECHECK-001\",\n severity: \"CRITICAL\",\n category: \"LEGAL\",\n message: `Violación AEPD: La categoría opcional '${catId}' tiene default: true (casilla pre-marcada).`,\n details: \"Las directrices de la AEPD y el RGPD prohíben taxativamente las casillas preactivadas por defecto.\",\n remediation: \"Establece default: false en todas las categorías opcionales.\",\n });\n }\n }\n }\n\n // Policy links check\n const hasPrivacy = !!(config.policy?.privacyUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.privacyUrl)));\n const hasCookies = !!(config.policy?.cookiesUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.cookiesUrl)));\n\n if (!hasPrivacy) {\n findings.push({\n id: \"CONFIG_MISSING_PRIVACY_URL\",\n rule: \"RGPD-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Privacidad ('policy.privacyUrl').\",\n remediation: \"Añade policy.privacyUrl: '/politica-privacidad' en consent.json.\",\n });\n }\n\n if (!hasCookies) {\n findings.push({\n id: \"CONFIG_MISSING_COOKIES_URL\",\n rule: \"LSSI-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Cookies ('policy.cookiesUrl').\",\n remediation: \"Añade policy.cookiesUrl: '/politica-cookies' en consent.json.\",\n });\n }\n\n // Floating badge revocation check\n const badge = config.ui?.floatingBadge;\n if (badge === false) {\n findings.push({\n id: \"CONFIG_FLOATING_BADGE_DISABLED\",\n rule: \"AEPD-REVOCATION-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"El botón flotante de revocación ('ui.floatingBadge') está desactivado.\",\n details: \"La AEPD exige que retirar el consentimiento sea tan fácil como otorgarlo.\",\n remediation: \"Mantén el floatingBadge activado o asegúrate de incluir un botón persistente [data-consent-open] en el footer.\",\n });\n }\n }\n\n /**\n * Scan source code directory for unblocked third-party scripts.\n */\n static auditSourceDirectory(dirPath: string, findings: AuditFinding[]): number {\n let count = 0;\n const files = this.walkDir(dirPath);\n\n const validExtensions = new Set([\".html\", \".htm\", \".jsx\", \".tsx\", \".vue\", \".php\", \".js\", \".ts\", \".mjs\"]);\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!validExtensions.has(ext)) continue;\n if (\n filePath.includes(\"node_modules\") ||\n filePath.includes(\"dist\") ||\n filePath.includes(\".git\") ||\n filePath.includes(\"/audit/\") ||\n filePath.includes(\"\\\\audit\\\\\") ||\n filePath.includes(\"/presets/\") ||\n filePath.includes(\"\\\\presets\\\\\") ||\n filePath.includes(\"tests/\") ||\n filePath.includes(\"tests\\\\\")\n ) {\n continue;\n }\n\n count++;\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n\n // 1. Scan for <script ...> tags (including multiline tags)\n const scriptTagRegex = /<\\s*script\\b([^>]*)>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = scriptTagRegex.exec(content)) !== null) {\n const tagAttributes = match[1];\n const fullTag = match[0];\n const offset = match.index;\n const lineNumber = content.substring(0, offset).split(\"\\n\").length;\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (tagAttributes.includes(tracker.domain) || fullTag.includes(tracker.domain)) {\n const isBlocked =\n tagAttributes.includes('type=\"text/plain\"') ||\n tagAttributes.includes(\"type='text/plain'\") ||\n tagAttributes.includes(\"data-consent\") ||\n tagAttributes.includes(\"data-src\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Script de seguimiento no bloqueado detectado (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: lineNumber,\n details: fullTag.replace(/\\s+/g, \" \").trim(),\n remediation: `Modifica el script para usar type=\"text/plain\" data-src=\"...\" data-consent=\"${tracker.category}\" o envuélvelo en un <ConsentGate>.`,\n });\n }\n }\n }\n }\n\n // 2. Scan for raw script imports or trackers in JS/TS/JSX/TSX/Vue files (excluding blocked script blocks)\n // Remove all <script type=\"text/plain\" ...>...</script> blocks from raw inspection\n const sanitizedContent = content.replace(/<\\s*script\\b[^>]*type=[\"']text\\/plain[\"'][^>]*>[\\s\\S]*?<\\s*\\/\\s*script\\s*>/gi, \"\");\n const lines = sanitizedContent.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const rawLine = lines[i];\n const line = rawLine.trim();\n\n // Skip comments, metadata, or already handled script tags\n if (\n line.startsWith(\"//\") ||\n line.startsWith(\"*\") ||\n line.startsWith(\"/*\") ||\n line.includes(\"lssi-ignore\") ||\n line.includes(\"<script\") ||\n line.includes(\"data-src\") ||\n line.includes(\"type=\\\"text/plain\\\"\") ||\n line.includes(\"type='text/plain'\")\n ) {\n continue;\n }\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (line.includes(tracker.domain)) {\n const isBlocked =\n line.includes(\"ConsentGate\") ||\n line.includes(\"consentGate\") ||\n line.includes(\"useConsent\") ||\n line.includes(\"useConsentService\") ||\n line.includes(\"data-consent\") ||\n line.includes(\"text/plain\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Referencia a rastreador sin control de consentimiento detectada (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: i + 1,\n details: line.trim(),\n remediation: `Envuelve la invocación en un bloque condicional 'useConsent(\"${tracker.category}\")' o componente <ConsentGate>.`,\n });\n }\n }\n }\n }\n } catch {}\n }\n\n return count;\n }\n\n /**\n * Audit live URL by fetching HTML and verifying pre-consent isolation.\n */\n static async auditLiveUrl(url: string, findings: AuditFinding[]): Promise<void> {\n try {\n const res = await fetch(url, { headers: { \"User-Agent\": \"Solvenza-LSSI-Auditor/1.5.1\" } });\n if (!res.ok) {\n findings.push({\n id: \"URL_FETCH_FAILED\",\n rule: \"LSSI-URL-001\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `No se pudo conectar a la URL '${url}' (HTTP ${res.status}).`,\n });\n return;\n }\n\n const html = await res.text();\n\n // Check for raw unblocked tracker scripts in live HTML\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n const regex = new RegExp(`<script[^>]*src=[\"'][^\"']*${tracker.domain.replace(\".\", \"\\\\.\")}[^\"']*[\"'][^>]*>`, \"gi\");\n const matches = html.match(regex);\n if (matches) {\n for (const match of matches) {\n if (!match.includes('type=\"text/plain\"') && !match.includes(\"data-consent\")) {\n findings.push({\n id: \"URL_RAW_TRACKER_LEAK\",\n rule: \"LSSI-LEAK-001\",\n severity: \"CRITICAL\",\n category: \"LIVE_URL\",\n message: `Fuga de rastreador en tiempo de ejecución: script '${tracker.name}' cargado antes del consentimiento.`,\n details: match,\n remediation: `Asegúrate de que '${tracker.name}' se inyecte con type=\"text/plain\" data-consent=\"...\".`,\n });\n }\n }\n }\n }\n } catch (err: any) {\n findings.push({\n id: \"URL_CONNECT_ERROR\",\n rule: \"LSSI-URL-002\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `Error al escanear la URL '${url}': ${err.message}`,\n });\n }\n }\n\n /**\n * Generate GitHub Actions markdown step summary.\n */\n static formatGitHubSummary(report: AuditReport): string {\n const statusEmoji = report.compliant ? \"✅\" : \"❌\";\n const statusText = report.compliant\n ? \"**APROBADO (Cumplimiento LSSI / AEPD / RGPD Correcto)**\"\n : \"**RECHAZADO (Violaciones Críticas de Privacidad Detectadas)**\";\n\n let md = `## ${statusEmoji} Auditoría de Cumplimiento LSSI & AEPD - Solvenza Cookies Compliance\\n\\n`;\n md += `> **Resultado:** ${statusText} \\n`;\n md += `> **Puntuación de Cumplimiento:** **${report.score} / 100** \\n`;\n md += `> **Archivos Escaneados:** ${report.scannedFilesCount} | **Fecha:** ${new Date(report.timestamp).toUTCString()}\\n\\n`;\n\n md += `### 📊 Resumen de Hallazgos:\\n\\n`;\n md += `| Gravedad | Cantidad | Descripción |\\n`;\n md += `|---|---|---|\\n`;\n md += `| 🔴 **CRÍTICO** | **${report.summary.critical}** | Violaciones legales directas (riesgo de sanción AEPD) |\\n`;\n md += `| 🟡 **ADVERTENCIA** | **${report.summary.warnings}** | Recomendaciones técnicas y mejores prácticas |\\n`;\n md += `| 🔵 **INFO** | **${report.summary.info}** | Sugerencias de optimización |\\n\\n`;\n\n if (report.findings.length > 0) {\n md += `### 🔍 Detalle de Hallazgos:\\n\\n`;\n md += `| Regla | Gravedad | Categoría | Archivo/Línea | Mensaje | Solución |\\n`;\n md += `|---|---|---|---|---|---|\\n`;\n\n for (const f of report.findings) {\n const sevBadge = f.severity === \"CRITICAL\" ? \"🔴 CRÍTICO\" : f.severity === \"WARNING\" ? \"🟡 ADVERTENCIA\" : \"🔵 INFO\";\n const location = f.file ? `\\`${f.file}${f.line ? `:${f.line}` : \"\"}\\`` : \"-\";\n const remediation = f.remediation ? f.remediation.replace(/\\|/g, \"\\\\|\") : \"-\";\n const message = f.message.replace(/\\|/g, \"\\\\|\");\n md += `| \\`${f.rule}\\` | ${sevBadge} | ${f.category} | ${location} | ${message} | ${remediation} |\\n`;\n }\n md += `\\n`;\n } else {\n md += `🎉 **No se encontraron violaciones ni fugas de privacidad.** Todos los recursos de terceros cumplen con el aislamiento previo al consentimiento (LSSI art. 22.2).\\n\\n`;\n }\n\n return md;\n }\n\n private static walkDir(dir: string): string[] {\n let results: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n for (const file of list) {\n const fullPath = path.join(dir, file);\n const stat = fs.statSync(fullPath);\n if (stat && stat.isDirectory()) {\n results = results.concat(this.walkDir(fullPath));\n } else {\n results.push(fullPath);\n }\n }\n } catch {}\n return results;\n }\n}\n","import type { ServicePreset } from \"../core/types.js\";\n\n/**\n * Built-in service presets for common analytics, marketing, media, payments, and security tools.\n * Compliant with AEPD / GDPR / LSSI standards with pre-configured cookie names, storage keys,\n * providers, and default categories.\n */\nexport const SERVICE_PRESETS: Record<string, ServicePreset> = {\n // --- ANALYTICS ---\n ga4: {\n id: \"ga4\",\n category: \"analytics\",\n label: \"Google Analytics 4\",\n provider: \"Google LLC\",\n description: \"Medición de visitas, páginas vistas, eventos y comportamiento de navegación del usuario.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_ga\", duration: \"2 años\", purpose: \"Distinguir a los usuarios únicos en el sitio web.\" },\n { name: \"_ga_*\", duration: \"2 años\", purpose: \"Mantener el estado de la sesión de Google Analytics 4.\" },\n { name: \"_gid\", duration: \"24 horas\", purpose: \"Distinguir a los usuarios durante un único día.\" },\n { name: \"_gat*\", duration: \"1 minuto\", purpose: \"Limitar el porcentaje de solicitudes a Google Analytics.\" },\n ],\n storageKeys: [\"_ga_*\", \"google_analytics_*\"],\n localStorage: [\"_ga_*\", \"google_analytics_*\"],\n },\n google_analytics: {\n id: \"google_analytics\",\n category: \"analytics\",\n label: \"Google Analytics (Universal / GA4)\",\n provider: \"Google LLC\",\n description: \"Medición de visitas y estadísticas anónimas de tráfico web.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_ga\", duration: \"2 años\", purpose: \"Distinguir usuarios.\" },\n { name: \"_ga_*\", duration: \"2 años\", purpose: \"Estado de la sesión GA4.\" },\n { name: \"_gid\", duration: \"24 horas\", purpose: \"Identificador diario.\" },\n ],\n storageKeys: [\"_ga_*\"],\n },\n posthog: {\n id: \"posthog\",\n category: \"analytics\",\n label: \"PostHog Analytics\",\n provider: \"PostHog, Inc.\",\n description: \"Analítica de producto, registro de sesiones, mapas de ruta y embudos de interacción.\",\n policyUrl: \"https://posthog.com/privacy\",\n cookies: [\n { name: \"ph_*_posthog\", duration: \"1 año\", purpose: \"Persistencia de usuario e identificación de sesiones en PostHog.\" },\n ],\n storageKeys: [\"ph_*_posthog\"],\n localStorage: [\"ph_*_posthog\"],\n sessionStorage: [\"ph_*_posthog\"],\n },\n hotjar: {\n id: \"hotjar\",\n category: \"analytics\",\n label: \"Hotjar Heatmaps & Feedback\",\n provider: \"Hotjar Ltd.\",\n description: \"Mapas de calor de clics, desplazamientos, grabaciones de comportamiento y encuestas.\",\n policyUrl: \"https://www.hotjar.com/legal/policies/privacy/\",\n cookies: [\n { name: \"_hjSession*\", duration: \"30 minutos\", purpose: \"Mantener los datos de la sesión actual del usuario en Hotjar.\" },\n { name: \"_hjSessionUser*\", duration: \"1 año\", purpose: \"Mantener el ID de usuario anónimo en el navegador.\" },\n { name: \"_hjIncludedIn*\", duration: \"Sesión\", purpose: \"Determinar si el usuario está incluido en el muestreo de datos.\" },\n { name: \"_hjAbsoluteCanvasUrl\", duration: \"Sesión\", purpose: \"Almacenar la URL canónica de visualización.\" },\n { name: \"_hjTLDTest\", duration: \"Sesión\", purpose: \"Verificar el nivel de dominio para cookies.\" },\n ],\n storageKeys: [\"_hj*\", \"hj*\"],\n localStorage: [\"_hj*\"],\n sessionStorage: [\"_hj*\"],\n },\n clarity: {\n id: \"clarity\",\n category: \"analytics\",\n label: \"Microsoft Clarity\",\n provider: \"Microsoft Corporation\",\n description: \"Grabación de navegación y mapas térmicos de interacción sin datos personales.\",\n policyUrl: \"https://privacy.microsoft.com/privacystatement\",\n cookies: [\n { name: \"_clck\", duration: \"1 año\", purpose: \"Persistir el ID de usuario Clarity y sus preferencias de página.\" },\n { name: \"_clsk\", duration: \"24 horas\", purpose: \"Conectar múltiples visitas de página en una única sesión.\" },\n { name: \"CLID\", duration: \"1 año\", purpose: \"Identificador de navegador para Clarity.\" },\n { name: \"MUID\", duration: \"1 año\", purpose: \"Identificador de usuario único de Microsoft.\" },\n ],\n storageKeys: [\"_cl*\"],\n localStorage: [\"_cl*\"],\n },\n matomo: {\n id: \"matomo\",\n category: \"analytics\",\n label: \"Matomo Analytics\",\n provider: \"InnoCraft Ltd.\",\n description: \"Plataforma de analítica web local-first y centrada en la privacidad.\",\n policyUrl: \"https://matomo.org/privacy-policy/\",\n cookies: [\n { name: \"_pk_id*\", duration: \"13 meses\", purpose: \"Almacenar datos de usuario únicos como el ID de visitante.\" },\n { name: \"_pk_ses*\", duration: \"30 minutos\", purpose: \"Almacenar datos temporales de la sesión de navegación.\" },\n { name: \"_pk_ref*\", duration: \"6 meses\", purpose: \"Almacenar la información de atribución de procedencia.\" },\n ],\n storageKeys: [\"_pk_*\"],\n localStorage: [\"_pk_*\"],\n },\n plausible: {\n id: \"plausible\",\n category: \"analytics\",\n label: \"Plausible Analytics\",\n provider: \"Plausible Insights OÜ\",\n description: \"Analítica web ligera y respetuosa con la privacidad, 100% libre de cookies.\",\n policyUrl: \"https://plausible.io/data-policy\",\n cookies: [],\n storageKeys: [],\n },\n\n // --- MARKETING & ADVERTISING ---\n meta_pixel: {\n id: \"meta_pixel\",\n category: \"marketing\",\n label: \"Meta Pixel (Facebook)\",\n provider: \"Meta Platforms Ireland Ltd.\",\n description: \"Medición de conversiones, personalización y optimización de campañas publicitarias en Facebook e Instagram.\",\n policyUrl: \"https://www.facebook.com/privacy/policy\",\n cookies: [\n { name: \"_fbp\", duration: \"90 días\", purpose: \"Almacenar y rastrear visitas en los sitios web para segmentación publicitaria.\" },\n { name: \"_fbc\", duration: \"90 días\", purpose: \"Guardar el último clic en un anuncio de Facebook (parámetro fbclid).\" },\n { name: \"fr\", duration: \"90 días\", purpose: \"Cookie de publicidad comportamental de Facebook.\" },\n { name: \"tr\", duration: \"Sesión\", purpose: \"Píxel de seguimiento de eventos en tiempo real.\" },\n { name: \"datr\", duration: \"2 años\", purpose: \"Identificación del navegador web para seguridad y analítica publicitaria.\" },\n ],\n storageKeys: [\"_fbp*\"],\n localStorage: [\"_fbp*\"],\n },\n facebook_pixel: {\n id: \"facebook_pixel\",\n category: \"marketing\",\n label: \"Meta Pixel (Facebook)\",\n provider: \"Meta Platforms Ireland Ltd.\",\n description: \"Medición de conversiones y retargeting en Meta/Facebook.\",\n policyUrl: \"https://www.facebook.com/privacy/policy\",\n cookies: [\n { name: \"_fbp\", duration: \"90 días\", purpose: \"Rastreo de conversiones y atribución de campañas.\" },\n { name: \"_fbc\", duration: \"90 días\", purpose: \"Identificador de clic de anuncio.\" },\n ],\n storageKeys: [\"_fbp*\"],\n },\n google_ads: {\n id: \"google_ads\",\n category: \"marketing\",\n label: \"Google Ads & Remarketing\",\n provider: \"Google LLC\",\n description: \"Medición de conversiones publicitarias y campañas de retargeting de Google Ads.\",\n policyUrl: \"https://policies.google.com/technologies/ads\",\n cookies: [\n { name: \"_gcl_au\", duration: \"90 días\", purpose: \"Medición de conversiones publicitarias de Google AdSense y Ads.\" },\n { name: \"_gcl_aw\", duration: \"90 días\", purpose: \"Conversiones procedentes de clics en anuncios de Google Ads.\" },\n { name: \"IDE\", duration: \"1 año\", purpose: \"Publicidad dirigida y medición de rendimiento de DoubleClick.\" },\n { name: \"DSID\", duration: \"2 semanas\", purpose: \"Identificar usuario conectado en sitios web ajenos a Google.\" },\n { name: \"RUL\", duration: \"1 año\", purpose: \"Audiencias de remarketing de Google Ads.\" },\n ],\n storageKeys: [\"_gcl_*\"],\n },\n tiktok_pixel: {\n id: \"tiktok_pixel\",\n category: \"marketing\",\n label: \"TikTok Pixel\",\n provider: \"TikTok Technology Limited\",\n description: \"Medición de rendimiento y segmentación de campañas publicitarias en TikTok.\",\n policyUrl: \"https://www.tiktok.com/legal/privacy-policy-eea\",\n cookies: [\n { name: \"_tt_enable_cookie\", duration: \"13 meses\", purpose: \"Habilitar la medición de conversiones del píxel de TikTok.\" },\n { name: \"_ttp\", duration: \"13 meses\", purpose: \"Medir y mejorar el rendimiento de las campañas publicitarias en TikTok.\" },\n ],\n storageKeys: [\"tt_*\", \"_ttp*\"],\n },\n linkedin_insight: {\n id: \"linkedin_insight\",\n category: \"marketing\",\n label: \"LinkedIn Insight Tag\",\n provider: \"LinkedIn Ireland Unlimited Company\",\n description: \"Informes de conversiones y atribución de campañas profesionales B2B en LinkedIn.\",\n policyUrl: \"https://www.linkedin.com/legal/privacy-policy\",\n cookies: [\n { name: \"li_sugr\", duration: \"90 días\", purpose: \"Identificador probabilístico de navegador en LinkedIn.\" },\n { name: \"bcookie\", duration: \"1 año\", purpose: \"Identificador de sesión de navegador para servicios de LinkedIn.\" },\n { name: \"lidc\", duration: \"24 horas\", purpose: \"Enrutamiento de centros de datos para LinkedIn.\" },\n { name: \"UserMatchHistory\", duration: \"30 días\", purpose: \"Sincronización de identificadores de publicidad de LinkedIn.\" },\n ],\n },\n twitter_pixel: {\n id: \"twitter_pixel\",\n category: \"marketing\",\n label: \"X / Twitter Ads Pixel\",\n provider: \"X Corp.\",\n description: \"Medición de conversiones e interacción con campañas publicitarias en X (Twitter).\",\n policyUrl: \"https://twitter.com/privacy\",\n cookies: [\n { name: \"personalization_id\", duration: \"2 años\", purpose: \"Personalización de publicidad y medición de eventos en X.\" },\n { name: \"muc_ads\", duration: \"2 años\", purpose: \"Seguimiento de conversiones publicitarias en X.\" },\n ],\n },\n hubspot: {\n id: \"hubspot\",\n category: \"marketing\",\n label: \"HubSpot CRM & Tracking\",\n provider: \"HubSpot, Inc.\",\n description: \"Analítica de leads, formularios inteligentes y seguimiento de clientes potenciales.\",\n policyUrl: \"https://legal.hubspot.com/privacy-policy\",\n cookies: [\n { name: \"__hstc\", duration: \"6 meses\", purpose: \"Rastreo de visitantes únicos, sesiones y marcas de tiempo.\" },\n { name: \"hubspotutk\", duration: \"6 meses\", purpose: \"Rastreo de identidad de visitante pasado a formularios.\" },\n { name: \"__hssc\", duration: \"30 minutos\", purpose: \"Rastreo de sesiones activas en HubSpot.\" },\n { name: \"__hssrc\", duration: \"Sesión\", purpose: \"Determinar si el usuario ha reiniciado su navegador.\" },\n ],\n storageKeys: [\"__hs*\", \"hubspot*\"],\n },\n\n // --- EMBEDDED MEDIA ---\n youtube: {\n id: \"youtube\",\n category: \"marketing\",\n label: \"YouTube Video Player\",\n provider: \"Google LLC\",\n description: \"Reproducción de contenidos de vídeo integrados y almacenamiento de preferencias del reproductor.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"VISITOR_INFO1_LIVE\", duration: \"6 meses\", purpose: \"Estimar el ancho de banda del usuario en páginas con vídeos de YouTube.\" },\n { name: \"YSC\", duration: \"Sesión\", purpose: \"Registrar estadísticas de visualizaciones de vídeo de YouTube.\" },\n { name: \"PREF\", duration: \"8 meses\", purpose: \"Almacenar preferencias de configuración del reproductor.\" },\n { name: \"GPS\", duration: \"30 minutos\", purpose: \"Rastrear ubicación en dispositivos móviles.\" },\n ],\n storageKeys: [\"yt-*\", \"yt-remote-*\"],\n localStorage: [\"yt-*\", \"yt-remote-*\"],\n },\n vimeo: {\n id: \"vimeo\",\n category: \"marketing\",\n label: \"Vimeo Video Player\",\n provider: \"Vimeo, Inc.\",\n description: \"Reproducción de vídeos interactivos alojados en Vimeo y estadísticas de visualización.\",\n policyUrl: \"https://vimeo.com/privacy\",\n cookies: [\n { name: \"vuid\", duration: \"2 años\", purpose: \"Almacenar el historial de reproducciones de vídeo del usuario en Vimeo.\" },\n { name: \"player\", duration: \"1 año\", purpose: \"Guardar las preferencias de volumen y resolución del reproductor.\" },\n ],\n storageKeys: [\"vimeo*\"],\n },\n spotify: {\n id: \"spotify\",\n category: \"marketing\",\n label: \"Spotify Player\",\n provider: \"Spotify AB\",\n description: \"Reproducción embebida de canciones, podcasts y listas de reproducción de Spotify.\",\n policyUrl: \"https://www.spotify.com/legal/privacy-policy/\",\n cookies: [\n { name: \"sp_t\", duration: \"1 año\", purpose: \"Identificador de usuario único de Spotify para contenido embebido.\" },\n { name: \"sp_m\", duration: \"1 año\", purpose: \"Preferencias de reproducción y cookies de sesión.\" },\n ],\n },\n\n // --- CHAT & CUSTOMER SUPPORT ---\n intercom: {\n id: \"intercom\",\n category: \"marketing\",\n label: \"Intercom Messenger\",\n provider: \"Intercom R&D Unlimited Company\",\n description: \"Widget de mensajería, soporte al cliente en tiempo real y asistencia guiada.\",\n policyUrl: \"https://www.intercom.com/legal/privacy\",\n cookies: [\n { name: \"intercom-id-*\", duration: \"9 meses\", purpose: \"Identificador anónimo de visitante para conversaciones en Intercom.\" },\n { name: \"intercom-session-*\", duration: \"7 días\", purpose: \"Persistir la sesión de chat activa.\" },\n { name: \"intercom-device-id-*\", duration: \"9 meses\", purpose: \"Identificador del dispositivo del visitante.\" },\n ],\n storageKeys: [\"intercom*\"],\n },\n crisp: {\n id: \"crisp\",\n category: \"marketing\",\n label: \"Crisp Live Chat\",\n provider: \"Crisp IM SARL\",\n description: \"Chat en vivo de soporte y mensajería multicanal para visitantes.\",\n policyUrl: \"https://crisp.chat/privacy\",\n cookies: [\n { name: \"crisp-client/*\", duration: \"6 meses\", purpose: \"Identificador de sesión de chat en Crisp.\" },\n ],\n storageKeys: [\"crisp-client*\"],\n },\n\n // --- NECESSARY / TECHNICAL / SECURITY ---\n gtm: {\n id: \"gtm\",\n category: \"necessary\",\n label: \"Google Tag Manager\",\n provider: \"Google LLC\",\n description: \"Contenedor técnico para la inyección y gestión centralizada de scripts del sitio web.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_gtm_*\", duration: \"Sesión\", purpose: \"Depuración técnica de contenedores de Google Tag Manager.\" },\n ],\n },\n google_recaptcha: {\n id: \"google_recaptcha\",\n category: \"necessary\",\n label: \"Google reCAPTCHA\",\n provider: \"Google LLC\",\n description: \"Protección contra bots automatizados, ataques de fuerza bruta y spam en formularios.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_GRECAPTCHA\", duration: \"6 meses\", purpose: \"Evaluación de riesgo de bots para protección de formularios.\" },\n { name: \"rc::a\", duration: \"Persistente\", purpose: \"Distinguir entre humanos y bots automatizados.\" },\n { name: \"rc::b\", duration: \"Sesión\", purpose: \"Distinguir entre humanos y bots automatizados.\" },\n { name: \"rc::c\", duration: \"Sesión\", purpose: \"Distinguir entre humanos y bots automatizados.\" },\n ],\n },\n cloudflare: {\n id: \"cloudflare\",\n category: \"necessary\",\n label: \"Cloudflare Security & Turnstile\",\n provider: \"Cloudflare, Inc.\",\n description: \"Mitigación de ataques DDoS, balanceo de carga CDN y validación de seguridad Turnstile.\",\n policyUrl: \"https://www.cloudflare.com/privacypolicy/\",\n cookies: [\n { name: \"__cf_bm\", duration: \"30 minutos\", purpose: \"Gestión de bots y mitigación de tráfico malicioso en Cloudflare.\" },\n { name: \"cf_clearance\", duration: \"1 año\", purpose: \"Autorización de paso tras superar desafío de seguridad Turnstile/Cloudflare.\" },\n ],\n },\n stripe: {\n id: \"stripe\",\n category: \"necessary\",\n label: \"Stripe Payments & Fraud Prevention\",\n provider: \"Stripe, Inc.\",\n description: \"Procesamiento seguro de transacciones bancarias, tarjetas y prevención de fraude financiero.\",\n policyUrl: \"https://stripe.com/privacy\",\n cookies: [\n { name: \"__stripe_mid\", duration: \"1 año\", purpose: \"Prevención de fraude y autenticación de pagos en Stripe.\" },\n { name: \"__stripe_sid\", duration: \"30 minutos\", purpose: \"Identificador de sesión de transacción en Stripe.\" },\n { name: \"m\", duration: \"2 años\", purpose: \"Detección de fraude financiero en la pasarela de pagos Stripe.\" },\n ],\n storageKeys: [\"__stripe_*\"],\n localStorage: [\"__stripe_*\"],\n },\n paypal: {\n id: \"paypal\",\n category: \"necessary\",\n label: \"PayPal Checkout\",\n provider: \"PayPal (Europe) S.à r.l. et Cie, S.C.A.\",\n description: \"Procesamiento de pagos y pasarela de cobro segura de PayPal.\",\n policyUrl: \"https://www.paypal.com/webapps/mpp/ua/privacy-full\",\n cookies: [\n { name: \"ts\", duration: \"3 años\", purpose: \"Gestión segura de pagos y prevención de fraude en PayPal.\" },\n { name: \"ts_c\", duration: \"3 años\", purpose: \"Autenticación segura de usuario en la pasarela PayPal.\" },\n ],\n },\n};\n\nexport type KnownPresetId = keyof typeof SERVICE_PRESETS | string;\n","import type { ConsentConfig, ServiceConfig, ServicePreset } from \"../core/types.js\";\nimport { SERVICE_PRESETS, type KnownPresetId } from \"./presets-catalog.js\";\n\nexport { SERVICE_PRESETS, type KnownPresetId, type ServicePreset };\n\n/**\n * Check if a preset exists in the catalog.\n */\nexport function hasPreset(presetId: string): boolean {\n return Object.prototype.hasOwnProperty.call(SERVICE_PRESETS, presetId);\n}\n\n/**\n * Retrieve a service preset by identifier, optionally applying custom overrides.\n *\n * @param presetId - Identifier of the preset (e.g. 'ga4', 'meta_pixel', 'hotjar')\n * @param overrides - Optional overrides to customize category, label, cookies, etc.\n * @returns Complete ServiceConfig ready for ConsentConfig.services\n *\n * @example\n * ```ts\n * const ga4Service = getPreset('ga4');\n * const customPosthog = getPreset('posthog', { label: 'Internal Product Metrics' });\n * ```\n */\nexport function getPreset(\n presetId: KnownPresetId,\n overrides?: Partial<ServiceConfig>,\n): ServiceConfig {\n const preset = SERVICE_PRESETS[presetId];\n if (!preset) {\n throw new Error(\n `[ConsentSDK] Unknown service preset: '${presetId}'. Available presets: ${Object.keys(\n SERVICE_PRESETS,\n ).join(\", \")}`,\n );\n }\n\n const base: ServiceConfig = {\n category: preset.category,\n label: preset.label,\n provider: preset.provider,\n policyUrl: preset.policyUrl,\n description: preset.description,\n cookies: preset.cookies ? [...preset.cookies] : undefined,\n storageKeys: preset.storageKeys ? [...preset.storageKeys] : undefined,\n localStorage: preset.localStorage ? [...preset.localStorage] : undefined,\n sessionStorage: preset.sessionStorage ? [...preset.sessionStorage] : undefined,\n };\n\n if (!overrides) {\n return base;\n }\n\n return {\n ...base,\n ...overrides,\n cookies: overrides.cookies || base.cookies,\n storageKeys: overrides.storageKeys || base.storageKeys,\n localStorage: overrides.localStorage || base.localStorage,\n sessionStorage: overrides.sessionStorage || base.sessionStorage,\n };\n}\n\n/**\n * Define multiple services using preset shorthand, preset IDs, or full custom configurations.\n *\n * @example\n * ```ts\n * const services = defineServices({\n * ga4: 'ga4',\n * facebook: 'meta_pixel',\n * hotjar: { preset: 'hotjar', category: 'analytics' },\n * customApi: { category: 'necessary', label: 'Custom Auth API' }\n * });\n * ```\n */\nexport function defineServices(\n definitions: Record<string, KnownPresetId | (Partial<ServiceConfig> & { preset?: string })>,\n): Record<string, ServiceConfig> {\n const result: Record<string, ServiceConfig> = {};\n\n for (const [key, def] of Object.entries(definitions)) {\n if (typeof def === \"string\") {\n result[key] = getPreset(def);\n } else if (def && typeof def === \"object\") {\n if (def.preset) {\n const { preset, ...overrides } = def;\n result[key] = getPreset(preset, overrides);\n } else if (hasPreset(key)) {\n result[key] = getPreset(key, def);\n } else {\n result[key] = def as ServiceConfig;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Resolve any preset references in a ConsentConfig object in-place or return a new hydrated config.\n * Hydrates services configured with `\"preset\": \"ga4\"` or matching known preset keys.\n */\nexport function resolveConfigPresets(config: ConsentConfig): ConsentConfig {\n if (!config || !config.services || typeof config.services !== \"object\") {\n return config;\n }\n\n const hydratedServices: Record<string, ServiceConfig> = {};\n\n for (const [serviceKey, serviceDef] of Object.entries(config.services)) {\n if (!serviceDef || typeof serviceDef !== \"object\") {\n hydratedServices[serviceKey] = serviceDef;\n continue;\n }\n\n const presetName = serviceDef.preset || (hasPreset(serviceKey) && !serviceDef.category ? serviceKey : undefined);\n\n if (presetName && hasPreset(presetName)) {\n const preset = SERVICE_PRESETS[presetName];\n hydratedServices[serviceKey] = {\n category: serviceDef.category || preset.category,\n label: serviceDef.label || preset.label,\n provider: serviceDef.provider || preset.provider,\n policyUrl: serviceDef.policyUrl || preset.policyUrl,\n description: serviceDef.description || preset.description,\n cookies: serviceDef.cookies || (preset.cookies ? [...preset.cookies] : undefined),\n storageKeys: serviceDef.storageKeys || (preset.storageKeys ? [...preset.storageKeys] : undefined),\n localStorage: serviceDef.localStorage || (preset.localStorage ? [...preset.localStorage] : undefined),\n sessionStorage: serviceDef.sessionStorage || (preset.sessionStorage ? [...preset.sessionStorage] : undefined),\n ...serviceDef,\n };\n } else {\n hydratedServices[serviceKey] = serviceDef;\n }\n }\n\n return {\n ...config,\n services: hydratedServices,\n };\n}\n"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../src/audit/index.ts","../src/audit/auditor.ts","../src/presets/presets-catalog.ts","../src/presets/index.ts"],"sourcesContent":["export { LssiAuditor } from \"./auditor.js\";\nexport type {\n AuditReport,\n AuditFinding,\n AuditOptions,\n AuditSeverity,\n} from \"./types.js\";\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ConsentConfig } from \"../core/types.js\";\nimport { resolveConfigPresets } from \"../presets/index.js\";\nimport type { AuditFinding, AuditOptions, AuditReport } from \"./types.js\";\n\n/**\n * Common third-party tracking script domains that require explicit prior consent under LSSI art. 22.2.\n */\nconst KNOWN_TRACKER_PATTERNS = [\n { domain: \"googletagmanager.com/gtm.js\", name: \"Google Tag Manager\", category: \"necessary_or_analytics\" },\n { domain: \"googletagmanager.com/gtag\", name: \"Google Analytics / Ads Gtag\", category: \"analytics\" },\n { domain: \"google-analytics.com/analytics.js\", name: \"Google Universal Analytics\", category: \"analytics\" },\n { domain: \"connect.facebook.net\", name: \"Meta / Facebook Pixel\", category: \"marketing\" },\n { domain: \"static.hotjar.com\", name: \"Hotjar Heatmaps & Feedback\", category: \"analytics\" },\n { domain: \"clarity.ms/tag\", name: \"Microsoft Clarity\", category: \"analytics\" },\n { domain: \"analytics.tiktok.com\", name: \"TikTok Pixel\", category: \"marketing\" },\n { domain: \"snap.licdn.com/insight\", name: \"LinkedIn Insight Tag\", category: \"marketing\" },\n { domain: \"cdn.segment.com/analytics\", name: \"Segment Analytics\", category: \"analytics\" },\n { domain: \"app.posthog.com/static\", name: \"PostHog Analytics\", category: \"analytics\" },\n { domain: \"mc.yandex.ru/metrika\", name: \"Yandex Metrika\", category: \"analytics\" },\n];\n\nexport class LssiAuditor {\n /**\n * Run complete compliance audit on configuration, source code, and/or live URL.\n */\n static async audit(options: AuditOptions): Promise<AuditReport> {\n const findings: AuditFinding[] = [];\n let scannedFilesCount = 0;\n\n // 1. Audit Configuration\n let resolvedConfig: ConsentConfig | null = null;\n if (options.config) {\n if (typeof options.config === \"string\") {\n try {\n const raw = fs.readFileSync(path.resolve(options.config), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración: ${err.message}`,\n remediation: \"Verifica que la ruta al archivo consent.json sea válida y contenga JSON bien formado.\",\n });\n }\n } else {\n resolvedConfig = resolveConfigPresets(options.config);\n }\n } else if (options.configPath) {\n try {\n const raw = fs.readFileSync(path.resolve(options.configPath), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración en '${options.configPath}': ${err.message}`,\n remediation: \"Especifica una ruta válida con --config <ruta>.\",\n });\n }\n }\n\n if (resolvedConfig) {\n this.auditConfig(resolvedConfig, findings);\n }\n\n // 2. Audit Source Code Directory\n if (options.srcDir && fs.existsSync(path.resolve(options.srcDir))) {\n scannedFilesCount = this.auditSourceDirectory(path.resolve(options.srcDir), findings);\n }\n\n // 3. Audit Live URL if provided\n if (options.url) {\n await this.auditLiveUrl(options.url, findings);\n }\n\n // Calculate score and summary\n const criticalCount = findings.filter((f) => f.severity === \"CRITICAL\").length;\n const warningCount = findings.filter((f) => f.severity === \"WARNING\").length;\n const infoCount = findings.filter((f) => f.severity === \"INFO\").length;\n\n const penalty = criticalCount * 25 + warningCount * 10 + infoCount * 2;\n const score = Math.max(0, 100 - penalty);\n const compliant = criticalCount === 0;\n\n return {\n timestamp: new Date().toISOString(),\n compliant,\n score,\n summary: {\n critical: criticalCount,\n warnings: warningCount,\n info: infoCount,\n totalPassed: Math.max(0, 10 - criticalCount - warningCount),\n },\n findings,\n scannedFilesCount,\n };\n }\n\n /**\n * Validate consent configuration against LSSI art. 22.2 and AEPD guidelines.\n */\n static auditConfig(config: ConsentConfig, findings: AuditFinding[]): void {\n if (!config.categories || typeof config.categories !== \"object\") {\n findings.push({\n id: \"CONFIG_NO_CATEGORIES\",\n rule: \"AEPD-CATEGORIES-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"No se encontraron categorías de consentimiento definidas.\",\n remediation: \"Declara al menos la categoría 'necessary' y las categorías opcionales ('analytics', 'marketing').\",\n });\n return;\n }\n\n // Necessary category check\n const nec = config.categories[\"necessary\"];\n if (!nec) {\n findings.push({\n id: \"CONFIG_MISSING_NECESSARY\",\n rule: \"LSSI-NECESSARY-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"Falta la categoría obligatoria 'necessary' (cookies técnicas y de seguridad).\",\n remediation: \"Añade 'necessary: { required: true, label: \\\"Necesarias\\\", description: \\\"...\\\" }'.\",\n });\n } else if (nec.required !== true) {\n findings.push({\n id: \"CONFIG_NECESSARY_NOT_REQUIRED\",\n rule: \"LSSI-NECESSARY-002\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"La categoría 'necessary' debe tener siempre 'required: true'.\",\n remediation: \"Establece required: true en la categoría necessary.\",\n });\n }\n\n // AEPD Legal Guardrail: NO pre-checked optional boxes allowed\n for (const [catId, cat] of Object.entries(config.categories)) {\n if (catId !== \"necessary\" && !cat.required) {\n if (cat.default === true) {\n findings.push({\n id: \"CONFIG_PRECHECKED_OPTIONAL\",\n rule: \"AEPD-PRECHECK-001\",\n severity: \"CRITICAL\",\n category: \"LEGAL\",\n message: `Violación AEPD: La categoría opcional '${catId}' tiene default: true (casilla pre-marcada).`,\n details: \"Las directrices de la AEPD y el RGPD prohíben taxativamente las casillas preactivadas por defecto.\",\n remediation: \"Establece default: false en todas las categorías opcionales.\",\n });\n }\n }\n }\n\n // Policy links check\n const hasPrivacy = !!(config.policy?.privacyUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.privacyUrl)));\n const hasCookies = !!(config.policy?.cookiesUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.cookiesUrl)));\n\n if (!hasPrivacy) {\n findings.push({\n id: \"CONFIG_MISSING_PRIVACY_URL\",\n rule: \"RGPD-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Privacidad ('policy.privacyUrl').\",\n remediation: \"Añade policy.privacyUrl: '/politica-privacidad' en consent.json.\",\n });\n }\n\n if (!hasCookies) {\n findings.push({\n id: \"CONFIG_MISSING_COOKIES_URL\",\n rule: \"LSSI-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Cookies ('policy.cookiesUrl').\",\n remediation: \"Añade policy.cookiesUrl: '/politica-cookies' en consent.json.\",\n });\n }\n\n // Floating badge revocation check\n const badge = config.ui?.floatingBadge;\n if (badge === false) {\n findings.push({\n id: \"CONFIG_FLOATING_BADGE_DISABLED\",\n rule: \"AEPD-REVOCATION-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"El botón flotante de revocación ('ui.floatingBadge') está desactivado.\",\n details: \"La AEPD exige que retirar el consentimiento sea tan fácil como otorgarlo.\",\n remediation: \"Mantén el floatingBadge activado o asegúrate de incluir un botón persistente [data-consent-open] en el footer.\",\n });\n }\n }\n\n /**\n * Scan source code directory for unblocked third-party scripts.\n */\n static auditSourceDirectory(dirPath: string, findings: AuditFinding[]): number {\n let count = 0;\n const files = this.walkDir(dirPath);\n\n const validExtensions = new Set([\".html\", \".htm\", \".jsx\", \".tsx\", \".vue\", \".php\", \".js\", \".ts\", \".mjs\"]);\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!validExtensions.has(ext)) continue;\n if (\n filePath.includes(\"node_modules\") ||\n filePath.includes(\"dist\") ||\n filePath.includes(\".git\") ||\n filePath.includes(\"/audit/\") ||\n filePath.includes(\"\\\\audit\\\\\") ||\n filePath.includes(\"/presets/\") ||\n filePath.includes(\"\\\\presets\\\\\") ||\n filePath.includes(\"tests/\") ||\n filePath.includes(\"tests\\\\\")\n ) {\n continue;\n }\n\n count++;\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n\n // 1. Scan for <script ...> tags (including multiline tags)\n const scriptTagRegex = /<\\s*script\\b([^>]*)>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = scriptTagRegex.exec(content)) !== null) {\n const tagAttributes = match[1];\n const fullTag = match[0];\n const offset = match.index;\n const lineNumber = content.substring(0, offset).split(\"\\n\").length;\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (tagAttributes.includes(tracker.domain) || fullTag.includes(tracker.domain)) {\n const isBlocked =\n tagAttributes.includes('type=\"text/plain\"') ||\n tagAttributes.includes(\"type='text/plain'\") ||\n tagAttributes.includes(\"data-consent\") ||\n tagAttributes.includes(\"data-src\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Script de seguimiento no bloqueado detectado (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: lineNumber,\n details: fullTag.replace(/\\s+/g, \" \").trim(),\n remediation: `Modifica el script para usar type=\"text/plain\" data-src=\"...\" data-consent=\"${tracker.category}\" o envuélvelo en un <ConsentGate>.`,\n });\n }\n }\n }\n }\n\n // 2. Scan for raw script imports or trackers in JS/TS/JSX/TSX/Vue files (excluding blocked script blocks)\n // Remove all <script type=\"text/plain\" ...>...</script> blocks from raw inspection\n const sanitizedContent = content.replace(/<\\s*script\\b[^>]*type=[\"']text\\/plain[\"'][^>]*>[\\s\\S]*?<\\s*\\/\\s*script\\s*>/gi, \"\");\n const lines = sanitizedContent.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const rawLine = lines[i];\n const line = rawLine.trim();\n\n // Skip comments, metadata, or already handled script tags\n if (\n line.startsWith(\"//\") ||\n line.startsWith(\"*\") ||\n line.startsWith(\"/*\") ||\n line.includes(\"lssi-ignore\") ||\n line.includes(\"<script\") ||\n line.includes(\"data-src\") ||\n line.includes(\"type=\\\"text/plain\\\"\") ||\n line.includes(\"type='text/plain'\")\n ) {\n continue;\n }\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (line.includes(tracker.domain)) {\n const isBlocked =\n line.includes(\"ConsentGate\") ||\n line.includes(\"consentGate\") ||\n line.includes(\"useConsent\") ||\n line.includes(\"useConsentService\") ||\n line.includes(\"data-consent\") ||\n line.includes(\"text/plain\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Referencia a rastreador sin control de consentimiento detectada (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: i + 1,\n details: line.trim(),\n remediation: `Envuelve la invocación en un bloque condicional 'useConsent(\"${tracker.category}\")' o componente <ConsentGate>.`,\n });\n }\n }\n }\n }\n } catch {}\n }\n\n return count;\n }\n\n /**\n * Audit live URL by fetching HTML and verifying pre-consent isolation.\n */\n static async auditLiveUrl(url: string, findings: AuditFinding[]): Promise<void> {\n try {\n const res = await fetch(url, { headers: { \"User-Agent\": \"Solvenza-LSSI-Auditor/1.5.3\" } });\n if (!res.ok) {\n findings.push({\n id: \"URL_FETCH_FAILED\",\n rule: \"LSSI-URL-001\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `No se pudo conectar a la URL '${url}' (HTTP ${res.status}).`,\n });\n return;\n }\n\n const html = await res.text();\n\n // Check for raw unblocked tracker scripts in live HTML\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n const regex = new RegExp(`<script[^>]*src=[\"'][^\"']*${tracker.domain.replace(\".\", \"\\\\.\")}[^\"']*[\"'][^>]*>`, \"gi\");\n const matches = html.match(regex);\n if (matches) {\n for (const match of matches) {\n if (!match.includes('type=\"text/plain\"') && !match.includes(\"data-consent\")) {\n findings.push({\n id: \"URL_RAW_TRACKER_LEAK\",\n rule: \"LSSI-LEAK-001\",\n severity: \"CRITICAL\",\n category: \"LIVE_URL\",\n message: `Fuga de rastreador en tiempo de ejecución: script '${tracker.name}' cargado antes del consentimiento.`,\n details: match,\n remediation: `Asegúrate de que '${tracker.name}' se inyecte con type=\"text/plain\" data-consent=\"...\".`,\n });\n }\n }\n }\n }\n } catch (err: any) {\n findings.push({\n id: \"URL_CONNECT_ERROR\",\n rule: \"LSSI-URL-002\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `Error al escanear la URL '${url}': ${err.message}`,\n });\n }\n }\n\n /**\n * Generate GitHub Actions markdown step summary.\n */\n static formatGitHubSummary(report: AuditReport): string {\n const statusEmoji = report.compliant ? \"✅\" : \"❌\";\n const statusText = report.compliant\n ? \"**APROBADO (Cumplimiento LSSI / AEPD / RGPD Correcto)**\"\n : \"**RECHAZADO (Violaciones Críticas de Privacidad Detectadas)**\";\n\n let md = `## ${statusEmoji} Auditoría de Cumplimiento LSSI & AEPD - Solvenza Cookies Compliance\\n\\n`;\n md += `> **Resultado:** ${statusText} \\n`;\n md += `> **Puntuación de Cumplimiento:** **${report.score} / 100** \\n`;\n md += `> **Archivos Escaneados:** ${report.scannedFilesCount} | **Fecha:** ${new Date(report.timestamp).toUTCString()}\\n\\n`;\n\n md += `### 📊 Resumen de Hallazgos:\\n\\n`;\n md += `| Gravedad | Cantidad | Descripción |\\n`;\n md += `|---|---|---|\\n`;\n md += `| 🔴 **CRÍTICO** | **${report.summary.critical}** | Violaciones legales directas (riesgo de sanción AEPD) |\\n`;\n md += `| 🟡 **ADVERTENCIA** | **${report.summary.warnings}** | Recomendaciones técnicas y mejores prácticas |\\n`;\n md += `| 🔵 **INFO** | **${report.summary.info}** | Sugerencias de optimización |\\n\\n`;\n\n if (report.findings.length > 0) {\n md += `### 🔍 Detalle de Hallazgos:\\n\\n`;\n md += `| Regla | Gravedad | Categoría | Archivo/Línea | Mensaje | Solución |\\n`;\n md += `|---|---|---|---|---|---|\\n`;\n\n for (const f of report.findings) {\n const sevBadge = f.severity === \"CRITICAL\" ? \"🔴 CRÍTICO\" : f.severity === \"WARNING\" ? \"🟡 ADVERTENCIA\" : \"🔵 INFO\";\n const location = f.file ? `\\`${f.file}${f.line ? `:${f.line}` : \"\"}\\`` : \"-\";\n const remediation = f.remediation ? f.remediation.replace(/\\|/g, \"\\\\|\") : \"-\";\n const message = f.message.replace(/\\|/g, \"\\\\|\");\n md += `| \\`${f.rule}\\` | ${sevBadge} | ${f.category} | ${location} | ${message} | ${remediation} |\\n`;\n }\n md += `\\n`;\n } else {\n md += `🎉 **No se encontraron violaciones ni fugas de privacidad.** Todos los recursos de terceros cumplen con el aislamiento previo al consentimiento (LSSI art. 22.2).\\n\\n`;\n }\n\n return md;\n }\n\n private static walkDir(dir: string): string[] {\n let results: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n for (const file of list) {\n const fullPath = path.join(dir, file);\n const stat = fs.statSync(fullPath);\n if (stat && stat.isDirectory()) {\n results = results.concat(this.walkDir(fullPath));\n } else {\n results.push(fullPath);\n }\n }\n } catch {}\n return results;\n }\n}\n","import type { ServicePreset } from \"../core/types.js\";\n\n/**\n * Built-in service presets for common analytics, marketing, media, payments, and security tools.\n * Compliant with AEPD / GDPR / LSSI standards with pre-configured cookie names, storage keys,\n * providers, and default categories.\n */\nexport const SERVICE_PRESETS: Record<string, ServicePreset> = {\n // --- ANALYTICS ---\n ga4: {\n id: \"ga4\",\n category: \"analytics\",\n label: \"Google Analytics 4\",\n provider: \"Google LLC\",\n description: \"Medición de visitas, páginas vistas, eventos y comportamiento de navegación del usuario.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_ga\", duration: \"2 años\", purpose: \"Distinguir a los usuarios únicos en el sitio web.\" },\n { name: \"_ga_*\", duration: \"2 años\", purpose: \"Mantener el estado de la sesión de Google Analytics 4.\" },\n { name: \"_gid\", duration: \"24 horas\", purpose: \"Distinguir a los usuarios durante un único día.\" },\n { name: \"_gat*\", duration: \"1 minuto\", purpose: \"Limitar el porcentaje de solicitudes a Google Analytics.\" },\n ],\n storageKeys: [\"_ga_*\", \"google_analytics_*\"],\n localStorage: [\"_ga_*\", \"google_analytics_*\"],\n },\n google_analytics: {\n id: \"google_analytics\",\n category: \"analytics\",\n label: \"Google Analytics (Universal / GA4)\",\n provider: \"Google LLC\",\n description: \"Medición de visitas y estadísticas anónimas de tráfico web.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_ga\", duration: \"2 años\", purpose: \"Distinguir usuarios.\" },\n { name: \"_ga_*\", duration: \"2 años\", purpose: \"Estado de la sesión GA4.\" },\n { name: \"_gid\", duration: \"24 horas\", purpose: \"Identificador diario.\" },\n ],\n storageKeys: [\"_ga_*\"],\n },\n posthog: {\n id: \"posthog\",\n category: \"analytics\",\n label: \"PostHog Analytics\",\n provider: \"PostHog, Inc.\",\n description: \"Analítica de producto, registro de sesiones, mapas de ruta y embudos de interacción.\",\n policyUrl: \"https://posthog.com/privacy\",\n cookies: [\n { name: \"ph_*_posthog\", duration: \"1 año\", purpose: \"Persistencia de usuario e identificación de sesiones en PostHog.\" },\n ],\n storageKeys: [\"ph_*_posthog\"],\n localStorage: [\"ph_*_posthog\"],\n sessionStorage: [\"ph_*_posthog\"],\n },\n hotjar: {\n id: \"hotjar\",\n category: \"analytics\",\n label: \"Hotjar Heatmaps & Feedback\",\n provider: \"Hotjar Ltd.\",\n description: \"Mapas de calor de clics, desplazamientos, grabaciones de comportamiento y encuestas.\",\n policyUrl: \"https://www.hotjar.com/legal/policies/privacy/\",\n cookies: [\n { name: \"_hjSession*\", duration: \"30 minutos\", purpose: \"Mantener los datos de la sesión actual del usuario en Hotjar.\" },\n { name: \"_hjSessionUser*\", duration: \"1 año\", purpose: \"Mantener el ID de usuario anónimo en el navegador.\" },\n { name: \"_hjIncludedIn*\", duration: \"Sesión\", purpose: \"Determinar si el usuario está incluido en el muestreo de datos.\" },\n { name: \"_hjAbsoluteCanvasUrl\", duration: \"Sesión\", purpose: \"Almacenar la URL canónica de visualización.\" },\n { name: \"_hjTLDTest\", duration: \"Sesión\", purpose: \"Verificar el nivel de dominio para cookies.\" },\n ],\n storageKeys: [\"_hj*\", \"hj*\"],\n localStorage: [\"_hj*\"],\n sessionStorage: [\"_hj*\"],\n },\n clarity: {\n id: \"clarity\",\n category: \"analytics\",\n label: \"Microsoft Clarity\",\n provider: \"Microsoft Corporation\",\n description: \"Grabación de navegación y mapas térmicos de interacción sin datos personales.\",\n policyUrl: \"https://privacy.microsoft.com/privacystatement\",\n cookies: [\n { name: \"_clck\", duration: \"1 año\", purpose: \"Persistir el ID de usuario Clarity y sus preferencias de página.\" },\n { name: \"_clsk\", duration: \"24 horas\", purpose: \"Conectar múltiples visitas de página en una única sesión.\" },\n { name: \"CLID\", duration: \"1 año\", purpose: \"Identificador de navegador para Clarity.\" },\n { name: \"MUID\", duration: \"1 año\", purpose: \"Identificador de usuario único de Microsoft.\" },\n ],\n storageKeys: [\"_cl*\"],\n localStorage: [\"_cl*\"],\n },\n matomo: {\n id: \"matomo\",\n category: \"analytics\",\n label: \"Matomo Analytics\",\n provider: \"InnoCraft Ltd.\",\n description: \"Plataforma de analítica web local-first y centrada en la privacidad.\",\n policyUrl: \"https://matomo.org/privacy-policy/\",\n cookies: [\n { name: \"_pk_id*\", duration: \"13 meses\", purpose: \"Almacenar datos de usuario únicos como el ID de visitante.\" },\n { name: \"_pk_ses*\", duration: \"30 minutos\", purpose: \"Almacenar datos temporales de la sesión de navegación.\" },\n { name: \"_pk_ref*\", duration: \"6 meses\", purpose: \"Almacenar la información de atribución de procedencia.\" },\n ],\n storageKeys: [\"_pk_*\"],\n localStorage: [\"_pk_*\"],\n },\n plausible: {\n id: \"plausible\",\n category: \"analytics\",\n label: \"Plausible Analytics\",\n provider: \"Plausible Insights OÜ\",\n description: \"Analítica web ligera y respetuosa con la privacidad, 100% libre de cookies.\",\n policyUrl: \"https://plausible.io/data-policy\",\n cookies: [],\n storageKeys: [],\n },\n\n // --- MARKETING & ADVERTISING ---\n meta_pixel: {\n id: \"meta_pixel\",\n category: \"marketing\",\n label: \"Meta Pixel (Facebook)\",\n provider: \"Meta Platforms Ireland Ltd.\",\n description: \"Medición de conversiones, personalización y optimización de campañas publicitarias en Facebook e Instagram.\",\n policyUrl: \"https://www.facebook.com/privacy/policy\",\n cookies: [\n { name: \"_fbp\", duration: \"90 días\", purpose: \"Almacenar y rastrear visitas en los sitios web para segmentación publicitaria.\" },\n { name: \"_fbc\", duration: \"90 días\", purpose: \"Guardar el último clic en un anuncio de Facebook (parámetro fbclid).\" },\n { name: \"fr\", duration: \"90 días\", purpose: \"Cookie de publicidad comportamental de Facebook.\" },\n { name: \"tr\", duration: \"Sesión\", purpose: \"Píxel de seguimiento de eventos en tiempo real.\" },\n { name: \"datr\", duration: \"2 años\", purpose: \"Identificación del navegador web para seguridad y analítica publicitaria.\" },\n ],\n storageKeys: [\"_fbp*\"],\n localStorage: [\"_fbp*\"],\n },\n facebook_pixel: {\n id: \"facebook_pixel\",\n category: \"marketing\",\n label: \"Meta Pixel (Facebook)\",\n provider: \"Meta Platforms Ireland Ltd.\",\n description: \"Medición de conversiones y retargeting en Meta/Facebook.\",\n policyUrl: \"https://www.facebook.com/privacy/policy\",\n cookies: [\n { name: \"_fbp\", duration: \"90 días\", purpose: \"Rastreo de conversiones y atribución de campañas.\" },\n { name: \"_fbc\", duration: \"90 días\", purpose: \"Identificador de clic de anuncio.\" },\n ],\n storageKeys: [\"_fbp*\"],\n },\n google_ads: {\n id: \"google_ads\",\n category: \"marketing\",\n label: \"Google Ads & Remarketing\",\n provider: \"Google LLC\",\n description: \"Medición de conversiones publicitarias y campañas de retargeting de Google Ads.\",\n policyUrl: \"https://policies.google.com/technologies/ads\",\n cookies: [\n { name: \"_gcl_au\", duration: \"90 días\", purpose: \"Medición de conversiones publicitarias de Google AdSense y Ads.\" },\n { name: \"_gcl_aw\", duration: \"90 días\", purpose: \"Conversiones procedentes de clics en anuncios de Google Ads.\" },\n { name: \"IDE\", duration: \"1 año\", purpose: \"Publicidad dirigida y medición de rendimiento de DoubleClick.\" },\n { name: \"DSID\", duration: \"2 semanas\", purpose: \"Identificar usuario conectado en sitios web ajenos a Google.\" },\n { name: \"RUL\", duration: \"1 año\", purpose: \"Audiencias de remarketing de Google Ads.\" },\n ],\n storageKeys: [\"_gcl_*\"],\n },\n tiktok_pixel: {\n id: \"tiktok_pixel\",\n category: \"marketing\",\n label: \"TikTok Pixel\",\n provider: \"TikTok Technology Limited\",\n description: \"Medición de rendimiento y segmentación de campañas publicitarias en TikTok.\",\n policyUrl: \"https://www.tiktok.com/legal/privacy-policy-eea\",\n cookies: [\n { name: \"_tt_enable_cookie\", duration: \"13 meses\", purpose: \"Habilitar la medición de conversiones del píxel de TikTok.\" },\n { name: \"_ttp\", duration: \"13 meses\", purpose: \"Medir y mejorar el rendimiento de las campañas publicitarias en TikTok.\" },\n ],\n storageKeys: [\"tt_*\", \"_ttp*\"],\n },\n linkedin_insight: {\n id: \"linkedin_insight\",\n category: \"marketing\",\n label: \"LinkedIn Insight Tag\",\n provider: \"LinkedIn Ireland Unlimited Company\",\n description: \"Informes de conversiones y atribución de campañas profesionales B2B en LinkedIn.\",\n policyUrl: \"https://www.linkedin.com/legal/privacy-policy\",\n cookies: [\n { name: \"li_sugr\", duration: \"90 días\", purpose: \"Identificador probabilístico de navegador en LinkedIn.\" },\n { name: \"bcookie\", duration: \"1 año\", purpose: \"Identificador de sesión de navegador para servicios de LinkedIn.\" },\n { name: \"lidc\", duration: \"24 horas\", purpose: \"Enrutamiento de centros de datos para LinkedIn.\" },\n { name: \"UserMatchHistory\", duration: \"30 días\", purpose: \"Sincronización de identificadores de publicidad de LinkedIn.\" },\n ],\n },\n twitter_pixel: {\n id: \"twitter_pixel\",\n category: \"marketing\",\n label: \"X / Twitter Ads Pixel\",\n provider: \"X Corp.\",\n description: \"Medición de conversiones e interacción con campañas publicitarias en X (Twitter).\",\n policyUrl: \"https://twitter.com/privacy\",\n cookies: [\n { name: \"personalization_id\", duration: \"2 años\", purpose: \"Personalización de publicidad y medición de eventos en X.\" },\n { name: \"muc_ads\", duration: \"2 años\", purpose: \"Seguimiento de conversiones publicitarias en X.\" },\n ],\n },\n hubspot: {\n id: \"hubspot\",\n category: \"marketing\",\n label: \"HubSpot CRM & Tracking\",\n provider: \"HubSpot, Inc.\",\n description: \"Analítica de leads, formularios inteligentes y seguimiento de clientes potenciales.\",\n policyUrl: \"https://legal.hubspot.com/privacy-policy\",\n cookies: [\n { name: \"__hstc\", duration: \"6 meses\", purpose: \"Rastreo de visitantes únicos, sesiones y marcas de tiempo.\" },\n { name: \"hubspotutk\", duration: \"6 meses\", purpose: \"Rastreo de identidad de visitante pasado a formularios.\" },\n { name: \"__hssc\", duration: \"30 minutos\", purpose: \"Rastreo de sesiones activas en HubSpot.\" },\n { name: \"__hssrc\", duration: \"Sesión\", purpose: \"Determinar si el usuario ha reiniciado su navegador.\" },\n ],\n storageKeys: [\"__hs*\", \"hubspot*\"],\n },\n\n // --- EMBEDDED MEDIA ---\n youtube: {\n id: \"youtube\",\n category: \"marketing\",\n label: \"YouTube Video Player\",\n provider: \"Google LLC\",\n description: \"Reproducción de contenidos de vídeo integrados y almacenamiento de preferencias del reproductor.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"VISITOR_INFO1_LIVE\", duration: \"6 meses\", purpose: \"Estimar el ancho de banda del usuario en páginas con vídeos de YouTube.\" },\n { name: \"YSC\", duration: \"Sesión\", purpose: \"Registrar estadísticas de visualizaciones de vídeo de YouTube.\" },\n { name: \"PREF\", duration: \"8 meses\", purpose: \"Almacenar preferencias de configuración del reproductor.\" },\n { name: \"GPS\", duration: \"30 minutos\", purpose: \"Rastrear ubicación en dispositivos móviles.\" },\n ],\n storageKeys: [\"yt-*\", \"yt-remote-*\"],\n localStorage: [\"yt-*\", \"yt-remote-*\"],\n },\n vimeo: {\n id: \"vimeo\",\n category: \"marketing\",\n label: \"Vimeo Video Player\",\n provider: \"Vimeo, Inc.\",\n description: \"Reproducción de vídeos interactivos alojados en Vimeo y estadísticas de visualización.\",\n policyUrl: \"https://vimeo.com/privacy\",\n cookies: [\n { name: \"vuid\", duration: \"2 años\", purpose: \"Almacenar el historial de reproducciones de vídeo del usuario en Vimeo.\" },\n { name: \"player\", duration: \"1 año\", purpose: \"Guardar las preferencias de volumen y resolución del reproductor.\" },\n ],\n storageKeys: [\"vimeo*\"],\n },\n spotify: {\n id: \"spotify\",\n category: \"marketing\",\n label: \"Spotify Player\",\n provider: \"Spotify AB\",\n description: \"Reproducción embebida de canciones, podcasts y listas de reproducción de Spotify.\",\n policyUrl: \"https://www.spotify.com/legal/privacy-policy/\",\n cookies: [\n { name: \"sp_t\", duration: \"1 año\", purpose: \"Identificador de usuario único de Spotify para contenido embebido.\" },\n { name: \"sp_m\", duration: \"1 año\", purpose: \"Preferencias de reproducción y cookies de sesión.\" },\n ],\n },\n\n // --- CHAT & CUSTOMER SUPPORT ---\n intercom: {\n id: \"intercom\",\n category: \"marketing\",\n label: \"Intercom Messenger\",\n provider: \"Intercom R&D Unlimited Company\",\n description: \"Widget de mensajería, soporte al cliente en tiempo real y asistencia guiada.\",\n policyUrl: \"https://www.intercom.com/legal/privacy\",\n cookies: [\n { name: \"intercom-id-*\", duration: \"9 meses\", purpose: \"Identificador anónimo de visitante para conversaciones en Intercom.\" },\n { name: \"intercom-session-*\", duration: \"7 días\", purpose: \"Persistir la sesión de chat activa.\" },\n { name: \"intercom-device-id-*\", duration: \"9 meses\", purpose: \"Identificador del dispositivo del visitante.\" },\n ],\n storageKeys: [\"intercom*\"],\n },\n crisp: {\n id: \"crisp\",\n category: \"marketing\",\n label: \"Crisp Live Chat\",\n provider: \"Crisp IM SARL\",\n description: \"Chat en vivo de soporte y mensajería multicanal para visitantes.\",\n policyUrl: \"https://crisp.chat/privacy\",\n cookies: [\n { name: \"crisp-client/*\", duration: \"6 meses\", purpose: \"Identificador de sesión de chat en Crisp.\" },\n ],\n storageKeys: [\"crisp-client*\"],\n },\n\n // --- NECESSARY / TECHNICAL / SECURITY ---\n gtm: {\n id: \"gtm\",\n category: \"necessary\",\n label: \"Google Tag Manager\",\n provider: \"Google LLC\",\n description: \"Contenedor técnico para la inyección y gestión centralizada de scripts del sitio web.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_gtm_*\", duration: \"Sesión\", purpose: \"Depuración técnica de contenedores de Google Tag Manager.\" },\n ],\n },\n google_recaptcha: {\n id: \"google_recaptcha\",\n category: \"necessary\",\n label: \"Google reCAPTCHA\",\n provider: \"Google LLC\",\n description: \"Protección contra bots automatizados, ataques de fuerza bruta y spam en formularios.\",\n policyUrl: \"https://policies.google.com/privacy\",\n cookies: [\n { name: \"_GRECAPTCHA\", duration: \"6 meses\", purpose: \"Evaluación de riesgo de bots para protección de formularios.\" },\n { name: \"rc::a\", duration: \"Persistente\", purpose: \"Distinguir entre humanos y bots automatizados.\" },\n { name: \"rc::b\", duration: \"Sesión\", purpose: \"Distinguir entre humanos y bots automatizados.\" },\n { name: \"rc::c\", duration: \"Sesión\", purpose: \"Distinguir entre humanos y bots automatizados.\" },\n ],\n },\n cloudflare: {\n id: \"cloudflare\",\n category: \"necessary\",\n label: \"Cloudflare Security & Turnstile\",\n provider: \"Cloudflare, Inc.\",\n description: \"Mitigación de ataques DDoS, balanceo de carga CDN y validación de seguridad Turnstile.\",\n policyUrl: \"https://www.cloudflare.com/privacypolicy/\",\n cookies: [\n { name: \"__cf_bm\", duration: \"30 minutos\", purpose: \"Gestión de bots y mitigación de tráfico malicioso en Cloudflare.\" },\n { name: \"cf_clearance\", duration: \"1 año\", purpose: \"Autorización de paso tras superar desafío de seguridad Turnstile/Cloudflare.\" },\n ],\n },\n stripe: {\n id: \"stripe\",\n category: \"necessary\",\n label: \"Stripe Payments & Fraud Prevention\",\n provider: \"Stripe, Inc.\",\n description: \"Procesamiento seguro de transacciones bancarias, tarjetas y prevención de fraude financiero.\",\n policyUrl: \"https://stripe.com/privacy\",\n cookies: [\n { name: \"__stripe_mid\", duration: \"1 año\", purpose: \"Prevención de fraude y autenticación de pagos en Stripe.\" },\n { name: \"__stripe_sid\", duration: \"30 minutos\", purpose: \"Identificador de sesión de transacción en Stripe.\" },\n { name: \"m\", duration: \"2 años\", purpose: \"Detección de fraude financiero en la pasarela de pagos Stripe.\" },\n ],\n storageKeys: [\"__stripe_*\"],\n localStorage: [\"__stripe_*\"],\n },\n paypal: {\n id: \"paypal\",\n category: \"necessary\",\n label: \"PayPal Checkout\",\n provider: \"PayPal (Europe) S.à r.l. et Cie, S.C.A.\",\n description: \"Procesamiento de pagos y pasarela de cobro segura de PayPal.\",\n policyUrl: \"https://www.paypal.com/webapps/mpp/ua/privacy-full\",\n cookies: [\n { name: \"ts\", duration: \"3 años\", purpose: \"Gestión segura de pagos y prevención de fraude en PayPal.\" },\n { name: \"ts_c\", duration: \"3 años\", purpose: \"Autenticación segura de usuario en la pasarela PayPal.\" },\n ],\n },\n};\n\nexport type KnownPresetId = keyof typeof SERVICE_PRESETS | string;\n","import type { ConsentConfig, ServiceConfig, ServicePreset } from \"../core/types.js\";\nimport { SERVICE_PRESETS, type KnownPresetId } from \"./presets-catalog.js\";\n\nexport { SERVICE_PRESETS, type KnownPresetId, type ServicePreset };\n\n/**\n * Check if a preset exists in the catalog.\n */\nexport function hasPreset(presetId: string): boolean {\n return Object.prototype.hasOwnProperty.call(SERVICE_PRESETS, presetId);\n}\n\n/**\n * Retrieve a service preset by identifier, optionally applying custom overrides.\n *\n * @param presetId - Identifier of the preset (e.g. 'ga4', 'meta_pixel', 'hotjar')\n * @param overrides - Optional overrides to customize category, label, cookies, etc.\n * @returns Complete ServiceConfig ready for ConsentConfig.services\n *\n * @example\n * ```ts\n * const ga4Service = getPreset('ga4');\n * const customPosthog = getPreset('posthog', { label: 'Internal Product Metrics' });\n * ```\n */\nexport function getPreset(\n presetId: KnownPresetId,\n overrides?: Partial<ServiceConfig>,\n): ServiceConfig {\n const preset = SERVICE_PRESETS[presetId];\n if (!preset) {\n throw new Error(\n `[ConsentSDK] Unknown service preset: '${presetId}'. Available presets: ${Object.keys(\n SERVICE_PRESETS,\n ).join(\", \")}`,\n );\n }\n\n const base: ServiceConfig = {\n category: preset.category,\n label: preset.label,\n provider: preset.provider,\n policyUrl: preset.policyUrl,\n description: preset.description,\n cookies: preset.cookies ? [...preset.cookies] : undefined,\n storageKeys: preset.storageKeys ? [...preset.storageKeys] : undefined,\n localStorage: preset.localStorage ? [...preset.localStorage] : undefined,\n sessionStorage: preset.sessionStorage ? [...preset.sessionStorage] : undefined,\n };\n\n if (!overrides) {\n return base;\n }\n\n return {\n ...base,\n ...overrides,\n cookies: overrides.cookies || base.cookies,\n storageKeys: overrides.storageKeys || base.storageKeys,\n localStorage: overrides.localStorage || base.localStorage,\n sessionStorage: overrides.sessionStorage || base.sessionStorage,\n };\n}\n\n/**\n * Define multiple services using preset shorthand, preset IDs, or full custom configurations.\n *\n * @example\n * ```ts\n * const services = defineServices({\n * ga4: 'ga4',\n * facebook: 'meta_pixel',\n * hotjar: { preset: 'hotjar', category: 'analytics' },\n * customApi: { category: 'necessary', label: 'Custom Auth API' }\n * });\n * ```\n */\nexport function defineServices(\n definitions: Record<string, KnownPresetId | (Partial<ServiceConfig> & { preset?: string })>,\n): Record<string, ServiceConfig> {\n const result: Record<string, ServiceConfig> = {};\n\n for (const [key, def] of Object.entries(definitions)) {\n if (typeof def === \"string\") {\n result[key] = getPreset(def);\n } else if (def && typeof def === \"object\") {\n if (def.preset) {\n const { preset, ...overrides } = def;\n result[key] = getPreset(preset, overrides);\n } else if (hasPreset(key)) {\n result[key] = getPreset(key, def);\n } else {\n result[key] = def as ServiceConfig;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Resolve any preset references in a ConsentConfig object in-place or return a new hydrated config.\n * Hydrates services configured with `\"preset\": \"ga4\"` or matching known preset keys.\n */\nexport function resolveConfigPresets(config: ConsentConfig): ConsentConfig {\n if (!config || !config.services || typeof config.services !== \"object\") {\n return config;\n }\n\n const hydratedServices: Record<string, ServiceConfig> = {};\n\n for (const [serviceKey, serviceDef] of Object.entries(config.services)) {\n if (!serviceDef || typeof serviceDef !== \"object\") {\n hydratedServices[serviceKey] = serviceDef;\n continue;\n }\n\n const presetName = serviceDef.preset || (hasPreset(serviceKey) && !serviceDef.category ? serviceKey : undefined);\n\n if (presetName && hasPreset(presetName)) {\n const preset = SERVICE_PRESETS[presetName];\n hydratedServices[serviceKey] = {\n category: serviceDef.category || preset.category,\n label: serviceDef.label || preset.label,\n provider: serviceDef.provider || preset.provider,\n policyUrl: serviceDef.policyUrl || preset.policyUrl,\n description: serviceDef.description || preset.description,\n cookies: serviceDef.cookies || (preset.cookies ? [...preset.cookies] : undefined),\n storageKeys: serviceDef.storageKeys || (preset.storageKeys ? [...preset.storageKeys] : undefined),\n localStorage: serviceDef.localStorage || (preset.localStorage ? [...preset.localStorage] : undefined),\n sessionStorage: serviceDef.sessionStorage || (preset.sessionStorage ? [...preset.sessionStorage] : undefined),\n ...serviceDef,\n };\n } else {\n hydratedServices[serviceKey] = serviceDef;\n }\n }\n\n return {\n ...config,\n services: hydratedServices,\n };\n}\n"],"mappings":"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","names":["_a"]}
package/dist/audit.js CHANGED
@@ -260,7 +260,7 @@ var LssiAuditor = class {
260
260
  */
261
261
  static async auditLiveUrl(url, findings) {
262
262
  try {
263
- const res = await fetch(url, { headers: { "User-Agent": "Solvenza-LSSI-Auditor/1.5.1" } });
263
+ const res = await fetch(url, { headers: { "User-Agent": "Solvenza-LSSI-Auditor/1.5.3" } });
264
264
  if (!res.ok) {
265
265
  findings.push({
266
266
  id: "URL_FETCH_FAILED",
package/dist/audit.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/audit/auditor.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ConsentConfig } from \"../core/types.js\";\nimport { resolveConfigPresets } from \"../presets/index.js\";\nimport type { AuditFinding, AuditOptions, AuditReport } from \"./types.js\";\n\n/**\n * Common third-party tracking script domains that require explicit prior consent under LSSI art. 22.2.\n */\nconst KNOWN_TRACKER_PATTERNS = [\n { domain: \"googletagmanager.com/gtm.js\", name: \"Google Tag Manager\", category: \"necessary_or_analytics\" },\n { domain: \"googletagmanager.com/gtag\", name: \"Google Analytics / Ads Gtag\", category: \"analytics\" },\n { domain: \"google-analytics.com/analytics.js\", name: \"Google Universal Analytics\", category: \"analytics\" },\n { domain: \"connect.facebook.net\", name: \"Meta / Facebook Pixel\", category: \"marketing\" },\n { domain: \"static.hotjar.com\", name: \"Hotjar Heatmaps & Feedback\", category: \"analytics\" },\n { domain: \"clarity.ms/tag\", name: \"Microsoft Clarity\", category: \"analytics\" },\n { domain: \"analytics.tiktok.com\", name: \"TikTok Pixel\", category: \"marketing\" },\n { domain: \"snap.licdn.com/insight\", name: \"LinkedIn Insight Tag\", category: \"marketing\" },\n { domain: \"cdn.segment.com/analytics\", name: \"Segment Analytics\", category: \"analytics\" },\n { domain: \"app.posthog.com/static\", name: \"PostHog Analytics\", category: \"analytics\" },\n { domain: \"mc.yandex.ru/metrika\", name: \"Yandex Metrika\", category: \"analytics\" },\n];\n\nexport class LssiAuditor {\n /**\n * Run complete compliance audit on configuration, source code, and/or live URL.\n */\n static async audit(options: AuditOptions): Promise<AuditReport> {\n const findings: AuditFinding[] = [];\n let scannedFilesCount = 0;\n\n // 1. Audit Configuration\n let resolvedConfig: ConsentConfig | null = null;\n if (options.config) {\n if (typeof options.config === \"string\") {\n try {\n const raw = fs.readFileSync(path.resolve(options.config), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración: ${err.message}`,\n remediation: \"Verifica que la ruta al archivo consent.json sea válida y contenga JSON bien formado.\",\n });\n }\n } else {\n resolvedConfig = resolveConfigPresets(options.config);\n }\n } else if (options.configPath) {\n try {\n const raw = fs.readFileSync(path.resolve(options.configPath), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración en '${options.configPath}': ${err.message}`,\n remediation: \"Especifica una ruta válida con --config <ruta>.\",\n });\n }\n }\n\n if (resolvedConfig) {\n this.auditConfig(resolvedConfig, findings);\n }\n\n // 2. Audit Source Code Directory\n if (options.srcDir && fs.existsSync(path.resolve(options.srcDir))) {\n scannedFilesCount = this.auditSourceDirectory(path.resolve(options.srcDir), findings);\n }\n\n // 3. Audit Live URL if provided\n if (options.url) {\n await this.auditLiveUrl(options.url, findings);\n }\n\n // Calculate score and summary\n const criticalCount = findings.filter((f) => f.severity === \"CRITICAL\").length;\n const warningCount = findings.filter((f) => f.severity === \"WARNING\").length;\n const infoCount = findings.filter((f) => f.severity === \"INFO\").length;\n\n const penalty = criticalCount * 25 + warningCount * 10 + infoCount * 2;\n const score = Math.max(0, 100 - penalty);\n const compliant = criticalCount === 0;\n\n return {\n timestamp: new Date().toISOString(),\n compliant,\n score,\n summary: {\n critical: criticalCount,\n warnings: warningCount,\n info: infoCount,\n totalPassed: Math.max(0, 10 - criticalCount - warningCount),\n },\n findings,\n scannedFilesCount,\n };\n }\n\n /**\n * Validate consent configuration against LSSI art. 22.2 and AEPD guidelines.\n */\n static auditConfig(config: ConsentConfig, findings: AuditFinding[]): void {\n if (!config.categories || typeof config.categories !== \"object\") {\n findings.push({\n id: \"CONFIG_NO_CATEGORIES\",\n rule: \"AEPD-CATEGORIES-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"No se encontraron categorías de consentimiento definidas.\",\n remediation: \"Declara al menos la categoría 'necessary' y las categorías opcionales ('analytics', 'marketing').\",\n });\n return;\n }\n\n // Necessary category check\n const nec = config.categories[\"necessary\"];\n if (!nec) {\n findings.push({\n id: \"CONFIG_MISSING_NECESSARY\",\n rule: \"LSSI-NECESSARY-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"Falta la categoría obligatoria 'necessary' (cookies técnicas y de seguridad).\",\n remediation: \"Añade 'necessary: { required: true, label: \\\"Necesarias\\\", description: \\\"...\\\" }'.\",\n });\n } else if (nec.required !== true) {\n findings.push({\n id: \"CONFIG_NECESSARY_NOT_REQUIRED\",\n rule: \"LSSI-NECESSARY-002\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"La categoría 'necessary' debe tener siempre 'required: true'.\",\n remediation: \"Establece required: true en la categoría necessary.\",\n });\n }\n\n // AEPD Legal Guardrail: NO pre-checked optional boxes allowed\n for (const [catId, cat] of Object.entries(config.categories)) {\n if (catId !== \"necessary\" && !cat.required) {\n if (cat.default === true) {\n findings.push({\n id: \"CONFIG_PRECHECKED_OPTIONAL\",\n rule: \"AEPD-PRECHECK-001\",\n severity: \"CRITICAL\",\n category: \"LEGAL\",\n message: `Violación AEPD: La categoría opcional '${catId}' tiene default: true (casilla pre-marcada).`,\n details: \"Las directrices de la AEPD y el RGPD prohíben taxativamente las casillas preactivadas por defecto.\",\n remediation: \"Establece default: false en todas las categorías opcionales.\",\n });\n }\n }\n }\n\n // Policy links check\n const hasPrivacy = !!(config.policy?.privacyUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.privacyUrl)));\n const hasCookies = !!(config.policy?.cookiesUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.cookiesUrl)));\n\n if (!hasPrivacy) {\n findings.push({\n id: \"CONFIG_MISSING_PRIVACY_URL\",\n rule: \"RGPD-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Privacidad ('policy.privacyUrl').\",\n remediation: \"Añade policy.privacyUrl: '/politica-privacidad' en consent.json.\",\n });\n }\n\n if (!hasCookies) {\n findings.push({\n id: \"CONFIG_MISSING_COOKIES_URL\",\n rule: \"LSSI-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Cookies ('policy.cookiesUrl').\",\n remediation: \"Añade policy.cookiesUrl: '/politica-cookies' en consent.json.\",\n });\n }\n\n // Floating badge revocation check\n const badge = config.ui?.floatingBadge;\n if (badge === false) {\n findings.push({\n id: \"CONFIG_FLOATING_BADGE_DISABLED\",\n rule: \"AEPD-REVOCATION-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"El botón flotante de revocación ('ui.floatingBadge') está desactivado.\",\n details: \"La AEPD exige que retirar el consentimiento sea tan fácil como otorgarlo.\",\n remediation: \"Mantén el floatingBadge activado o asegúrate de incluir un botón persistente [data-consent-open] en el footer.\",\n });\n }\n }\n\n /**\n * Scan source code directory for unblocked third-party scripts.\n */\n static auditSourceDirectory(dirPath: string, findings: AuditFinding[]): number {\n let count = 0;\n const files = this.walkDir(dirPath);\n\n const validExtensions = new Set([\".html\", \".htm\", \".jsx\", \".tsx\", \".vue\", \".php\", \".js\", \".ts\", \".mjs\"]);\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!validExtensions.has(ext)) continue;\n if (\n filePath.includes(\"node_modules\") ||\n filePath.includes(\"dist\") ||\n filePath.includes(\".git\") ||\n filePath.includes(\"/audit/\") ||\n filePath.includes(\"\\\\audit\\\\\") ||\n filePath.includes(\"/presets/\") ||\n filePath.includes(\"\\\\presets\\\\\") ||\n filePath.includes(\"tests/\") ||\n filePath.includes(\"tests\\\\\")\n ) {\n continue;\n }\n\n count++;\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n\n // 1. Scan for <script ...> tags (including multiline tags)\n const scriptTagRegex = /<\\s*script\\b([^>]*)>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = scriptTagRegex.exec(content)) !== null) {\n const tagAttributes = match[1];\n const fullTag = match[0];\n const offset = match.index;\n const lineNumber = content.substring(0, offset).split(\"\\n\").length;\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (tagAttributes.includes(tracker.domain) || fullTag.includes(tracker.domain)) {\n const isBlocked =\n tagAttributes.includes('type=\"text/plain\"') ||\n tagAttributes.includes(\"type='text/plain'\") ||\n tagAttributes.includes(\"data-consent\") ||\n tagAttributes.includes(\"data-src\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Script de seguimiento no bloqueado detectado (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: lineNumber,\n details: fullTag.replace(/\\s+/g, \" \").trim(),\n remediation: `Modifica el script para usar type=\"text/plain\" data-src=\"...\" data-consent=\"${tracker.category}\" o envuélvelo en un <ConsentGate>.`,\n });\n }\n }\n }\n }\n\n // 2. Scan for raw script imports or trackers in JS/TS/JSX/TSX/Vue files (excluding blocked script blocks)\n // Remove all <script type=\"text/plain\" ...>...</script> blocks from raw inspection\n const sanitizedContent = content.replace(/<\\s*script\\b[^>]*type=[\"']text\\/plain[\"'][^>]*>[\\s\\S]*?<\\s*\\/\\s*script\\s*>/gi, \"\");\n const lines = sanitizedContent.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const rawLine = lines[i];\n const line = rawLine.trim();\n\n // Skip comments, metadata, or already handled script tags\n if (\n line.startsWith(\"//\") ||\n line.startsWith(\"*\") ||\n line.startsWith(\"/*\") ||\n line.includes(\"lssi-ignore\") ||\n line.includes(\"<script\") ||\n line.includes(\"data-src\") ||\n line.includes(\"type=\\\"text/plain\\\"\") ||\n line.includes(\"type='text/plain'\")\n ) {\n continue;\n }\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (line.includes(tracker.domain)) {\n const isBlocked =\n line.includes(\"ConsentGate\") ||\n line.includes(\"consentGate\") ||\n line.includes(\"useConsent\") ||\n line.includes(\"useConsentService\") ||\n line.includes(\"data-consent\") ||\n line.includes(\"text/plain\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Referencia a rastreador sin control de consentimiento detectada (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: i + 1,\n details: line.trim(),\n remediation: `Envuelve la invocación en un bloque condicional 'useConsent(\"${tracker.category}\")' o componente <ConsentGate>.`,\n });\n }\n }\n }\n }\n } catch {}\n }\n\n return count;\n }\n\n /**\n * Audit live URL by fetching HTML and verifying pre-consent isolation.\n */\n static async auditLiveUrl(url: string, findings: AuditFinding[]): Promise<void> {\n try {\n const res = await fetch(url, { headers: { \"User-Agent\": \"Solvenza-LSSI-Auditor/1.5.1\" } });\n if (!res.ok) {\n findings.push({\n id: \"URL_FETCH_FAILED\",\n rule: \"LSSI-URL-001\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `No se pudo conectar a la URL '${url}' (HTTP ${res.status}).`,\n });\n return;\n }\n\n const html = await res.text();\n\n // Check for raw unblocked tracker scripts in live HTML\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n const regex = new RegExp(`<script[^>]*src=[\"'][^\"']*${tracker.domain.replace(\".\", \"\\\\.\")}[^\"']*[\"'][^>]*>`, \"gi\");\n const matches = html.match(regex);\n if (matches) {\n for (const match of matches) {\n if (!match.includes('type=\"text/plain\"') && !match.includes(\"data-consent\")) {\n findings.push({\n id: \"URL_RAW_TRACKER_LEAK\",\n rule: \"LSSI-LEAK-001\",\n severity: \"CRITICAL\",\n category: \"LIVE_URL\",\n message: `Fuga de rastreador en tiempo de ejecución: script '${tracker.name}' cargado antes del consentimiento.`,\n details: match,\n remediation: `Asegúrate de que '${tracker.name}' se inyecte con type=\"text/plain\" data-consent=\"...\".`,\n });\n }\n }\n }\n }\n } catch (err: any) {\n findings.push({\n id: \"URL_CONNECT_ERROR\",\n rule: \"LSSI-URL-002\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `Error al escanear la URL '${url}': ${err.message}`,\n });\n }\n }\n\n /**\n * Generate GitHub Actions markdown step summary.\n */\n static formatGitHubSummary(report: AuditReport): string {\n const statusEmoji = report.compliant ? \"✅\" : \"❌\";\n const statusText = report.compliant\n ? \"**APROBADO (Cumplimiento LSSI / AEPD / RGPD Correcto)**\"\n : \"**RECHAZADO (Violaciones Críticas de Privacidad Detectadas)**\";\n\n let md = `## ${statusEmoji} Auditoría de Cumplimiento LSSI & AEPD - Solvenza Cookies Compliance\\n\\n`;\n md += `> **Resultado:** ${statusText} \\n`;\n md += `> **Puntuación de Cumplimiento:** **${report.score} / 100** \\n`;\n md += `> **Archivos Escaneados:** ${report.scannedFilesCount} | **Fecha:** ${new Date(report.timestamp).toUTCString()}\\n\\n`;\n\n md += `### 📊 Resumen de Hallazgos:\\n\\n`;\n md += `| Gravedad | Cantidad | Descripción |\\n`;\n md += `|---|---|---|\\n`;\n md += `| 🔴 **CRÍTICO** | **${report.summary.critical}** | Violaciones legales directas (riesgo de sanción AEPD) |\\n`;\n md += `| 🟡 **ADVERTENCIA** | **${report.summary.warnings}** | Recomendaciones técnicas y mejores prácticas |\\n`;\n md += `| 🔵 **INFO** | **${report.summary.info}** | Sugerencias de optimización |\\n\\n`;\n\n if (report.findings.length > 0) {\n md += `### 🔍 Detalle de Hallazgos:\\n\\n`;\n md += `| Regla | Gravedad | Categoría | Archivo/Línea | Mensaje | Solución |\\n`;\n md += `|---|---|---|---|---|---|\\n`;\n\n for (const f of report.findings) {\n const sevBadge = f.severity === \"CRITICAL\" ? \"🔴 CRÍTICO\" : f.severity === \"WARNING\" ? \"🟡 ADVERTENCIA\" : \"🔵 INFO\";\n const location = f.file ? `\\`${f.file}${f.line ? `:${f.line}` : \"\"}\\`` : \"-\";\n const remediation = f.remediation ? f.remediation.replace(/\\|/g, \"\\\\|\") : \"-\";\n const message = f.message.replace(/\\|/g, \"\\\\|\");\n md += `| \\`${f.rule}\\` | ${sevBadge} | ${f.category} | ${location} | ${message} | ${remediation} |\\n`;\n }\n md += `\\n`;\n } else {\n md += `🎉 **No se encontraron violaciones ni fugas de privacidad.** Todos los recursos de terceros cumplen con el aislamiento previo al consentimiento (LSSI art. 22.2).\\n\\n`;\n }\n\n return md;\n }\n\n private static walkDir(dir: string): string[] {\n let results: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n for (const file of list) {\n const fullPath = path.join(dir, file);\n const stat = fs.statSync(fullPath);\n if (stat && stat.isDirectory()) {\n results = results.concat(this.walkDir(fullPath));\n } else {\n results.push(fullPath);\n }\n }\n } catch {}\n return results;\n }\n}\n"],"mappings":"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","names":["_a"]}
1
+ {"version":3,"sources":["../src/audit/auditor.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport type { ConsentConfig } from \"../core/types.js\";\nimport { resolveConfigPresets } from \"../presets/index.js\";\nimport type { AuditFinding, AuditOptions, AuditReport } from \"./types.js\";\n\n/**\n * Common third-party tracking script domains that require explicit prior consent under LSSI art. 22.2.\n */\nconst KNOWN_TRACKER_PATTERNS = [\n { domain: \"googletagmanager.com/gtm.js\", name: \"Google Tag Manager\", category: \"necessary_or_analytics\" },\n { domain: \"googletagmanager.com/gtag\", name: \"Google Analytics / Ads Gtag\", category: \"analytics\" },\n { domain: \"google-analytics.com/analytics.js\", name: \"Google Universal Analytics\", category: \"analytics\" },\n { domain: \"connect.facebook.net\", name: \"Meta / Facebook Pixel\", category: \"marketing\" },\n { domain: \"static.hotjar.com\", name: \"Hotjar Heatmaps & Feedback\", category: \"analytics\" },\n { domain: \"clarity.ms/tag\", name: \"Microsoft Clarity\", category: \"analytics\" },\n { domain: \"analytics.tiktok.com\", name: \"TikTok Pixel\", category: \"marketing\" },\n { domain: \"snap.licdn.com/insight\", name: \"LinkedIn Insight Tag\", category: \"marketing\" },\n { domain: \"cdn.segment.com/analytics\", name: \"Segment Analytics\", category: \"analytics\" },\n { domain: \"app.posthog.com/static\", name: \"PostHog Analytics\", category: \"analytics\" },\n { domain: \"mc.yandex.ru/metrika\", name: \"Yandex Metrika\", category: \"analytics\" },\n];\n\nexport class LssiAuditor {\n /**\n * Run complete compliance audit on configuration, source code, and/or live URL.\n */\n static async audit(options: AuditOptions): Promise<AuditReport> {\n const findings: AuditFinding[] = [];\n let scannedFilesCount = 0;\n\n // 1. Audit Configuration\n let resolvedConfig: ConsentConfig | null = null;\n if (options.config) {\n if (typeof options.config === \"string\") {\n try {\n const raw = fs.readFileSync(path.resolve(options.config), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración: ${err.message}`,\n remediation: \"Verifica que la ruta al archivo consent.json sea válida y contenga JSON bien formado.\",\n });\n }\n } else {\n resolvedConfig = resolveConfigPresets(options.config);\n }\n } else if (options.configPath) {\n try {\n const raw = fs.readFileSync(path.resolve(options.configPath), \"utf8\");\n resolvedConfig = resolveConfigPresets(JSON.parse(raw));\n } catch (err: any) {\n findings.push({\n id: \"CONFIG_FILE_ERROR\",\n rule: \"LSSI-CONFIG-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: `No se pudo leer el archivo de configuración en '${options.configPath}': ${err.message}`,\n remediation: \"Especifica una ruta válida con --config <ruta>.\",\n });\n }\n }\n\n if (resolvedConfig) {\n this.auditConfig(resolvedConfig, findings);\n }\n\n // 2. Audit Source Code Directory\n if (options.srcDir && fs.existsSync(path.resolve(options.srcDir))) {\n scannedFilesCount = this.auditSourceDirectory(path.resolve(options.srcDir), findings);\n }\n\n // 3. Audit Live URL if provided\n if (options.url) {\n await this.auditLiveUrl(options.url, findings);\n }\n\n // Calculate score and summary\n const criticalCount = findings.filter((f) => f.severity === \"CRITICAL\").length;\n const warningCount = findings.filter((f) => f.severity === \"WARNING\").length;\n const infoCount = findings.filter((f) => f.severity === \"INFO\").length;\n\n const penalty = criticalCount * 25 + warningCount * 10 + infoCount * 2;\n const score = Math.max(0, 100 - penalty);\n const compliant = criticalCount === 0;\n\n return {\n timestamp: new Date().toISOString(),\n compliant,\n score,\n summary: {\n critical: criticalCount,\n warnings: warningCount,\n info: infoCount,\n totalPassed: Math.max(0, 10 - criticalCount - warningCount),\n },\n findings,\n scannedFilesCount,\n };\n }\n\n /**\n * Validate consent configuration against LSSI art. 22.2 and AEPD guidelines.\n */\n static auditConfig(config: ConsentConfig, findings: AuditFinding[]): void {\n if (!config.categories || typeof config.categories !== \"object\") {\n findings.push({\n id: \"CONFIG_NO_CATEGORIES\",\n rule: \"AEPD-CATEGORIES-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"No se encontraron categorías de consentimiento definidas.\",\n remediation: \"Declara al menos la categoría 'necessary' y las categorías opcionales ('analytics', 'marketing').\",\n });\n return;\n }\n\n // Necessary category check\n const nec = config.categories[\"necessary\"];\n if (!nec) {\n findings.push({\n id: \"CONFIG_MISSING_NECESSARY\",\n rule: \"LSSI-NECESSARY-001\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"Falta la categoría obligatoria 'necessary' (cookies técnicas y de seguridad).\",\n remediation: \"Añade 'necessary: { required: true, label: \\\"Necesarias\\\", description: \\\"...\\\" }'.\",\n });\n } else if (nec.required !== true) {\n findings.push({\n id: \"CONFIG_NECESSARY_NOT_REQUIRED\",\n rule: \"LSSI-NECESSARY-002\",\n severity: \"CRITICAL\",\n category: \"CONFIG\",\n message: \"La categoría 'necessary' debe tener siempre 'required: true'.\",\n remediation: \"Establece required: true en la categoría necessary.\",\n });\n }\n\n // AEPD Legal Guardrail: NO pre-checked optional boxes allowed\n for (const [catId, cat] of Object.entries(config.categories)) {\n if (catId !== \"necessary\" && !cat.required) {\n if (cat.default === true) {\n findings.push({\n id: \"CONFIG_PRECHECKED_OPTIONAL\",\n rule: \"AEPD-PRECHECK-001\",\n severity: \"CRITICAL\",\n category: \"LEGAL\",\n message: `Violación AEPD: La categoría opcional '${catId}' tiene default: true (casilla pre-marcada).`,\n details: \"Las directrices de la AEPD y el RGPD prohíben taxativamente las casillas preactivadas por defecto.\",\n remediation: \"Establece default: false en todas las categorías opcionales.\",\n });\n }\n }\n }\n\n // Policy links check\n const hasPrivacy = !!(config.policy?.privacyUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.privacyUrl)));\n const hasCookies = !!(config.policy?.cookiesUrl || (config.translations && Object.values(config.translations).some((t) => t.policy?.cookiesUrl)));\n\n if (!hasPrivacy) {\n findings.push({\n id: \"CONFIG_MISSING_PRIVACY_URL\",\n rule: \"RGPD-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Privacidad ('policy.privacyUrl').\",\n remediation: \"Añade policy.privacyUrl: '/politica-privacidad' en consent.json.\",\n });\n }\n\n if (!hasCookies) {\n findings.push({\n id: \"CONFIG_MISSING_COOKIES_URL\",\n rule: \"LSSI-POLICY-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"No se ha configurado un enlace a la Política de Cookies ('policy.cookiesUrl').\",\n remediation: \"Añade policy.cookiesUrl: '/politica-cookies' en consent.json.\",\n });\n }\n\n // Floating badge revocation check\n const badge = config.ui?.floatingBadge;\n if (badge === false) {\n findings.push({\n id: \"CONFIG_FLOATING_BADGE_DISABLED\",\n rule: \"AEPD-REVOCATION-001\",\n severity: \"WARNING\",\n category: \"LEGAL\",\n message: \"El botón flotante de revocación ('ui.floatingBadge') está desactivado.\",\n details: \"La AEPD exige que retirar el consentimiento sea tan fácil como otorgarlo.\",\n remediation: \"Mantén el floatingBadge activado o asegúrate de incluir un botón persistente [data-consent-open] en el footer.\",\n });\n }\n }\n\n /**\n * Scan source code directory for unblocked third-party scripts.\n */\n static auditSourceDirectory(dirPath: string, findings: AuditFinding[]): number {\n let count = 0;\n const files = this.walkDir(dirPath);\n\n const validExtensions = new Set([\".html\", \".htm\", \".jsx\", \".tsx\", \".vue\", \".php\", \".js\", \".ts\", \".mjs\"]);\n\n for (const filePath of files) {\n const ext = path.extname(filePath).toLowerCase();\n if (!validExtensions.has(ext)) continue;\n if (\n filePath.includes(\"node_modules\") ||\n filePath.includes(\"dist\") ||\n filePath.includes(\".git\") ||\n filePath.includes(\"/audit/\") ||\n filePath.includes(\"\\\\audit\\\\\") ||\n filePath.includes(\"/presets/\") ||\n filePath.includes(\"\\\\presets\\\\\") ||\n filePath.includes(\"tests/\") ||\n filePath.includes(\"tests\\\\\")\n ) {\n continue;\n }\n\n count++;\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n\n // 1. Scan for <script ...> tags (including multiline tags)\n const scriptTagRegex = /<\\s*script\\b([^>]*)>/gi;\n let match: RegExpExecArray | null;\n\n while ((match = scriptTagRegex.exec(content)) !== null) {\n const tagAttributes = match[1];\n const fullTag = match[0];\n const offset = match.index;\n const lineNumber = content.substring(0, offset).split(\"\\n\").length;\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (tagAttributes.includes(tracker.domain) || fullTag.includes(tracker.domain)) {\n const isBlocked =\n tagAttributes.includes('type=\"text/plain\"') ||\n tagAttributes.includes(\"type='text/plain'\") ||\n tagAttributes.includes(\"data-consent\") ||\n tagAttributes.includes(\"data-src\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Script de seguimiento no bloqueado detectado (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: lineNumber,\n details: fullTag.replace(/\\s+/g, \" \").trim(),\n remediation: `Modifica el script para usar type=\"text/plain\" data-src=\"...\" data-consent=\"${tracker.category}\" o envuélvelo en un <ConsentGate>.`,\n });\n }\n }\n }\n }\n\n // 2. Scan for raw script imports or trackers in JS/TS/JSX/TSX/Vue files (excluding blocked script blocks)\n // Remove all <script type=\"text/plain\" ...>...</script> blocks from raw inspection\n const sanitizedContent = content.replace(/<\\s*script\\b[^>]*type=[\"']text\\/plain[\"'][^>]*>[\\s\\S]*?<\\s*\\/\\s*script\\s*>/gi, \"\");\n const lines = sanitizedContent.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const rawLine = lines[i];\n const line = rawLine.trim();\n\n // Skip comments, metadata, or already handled script tags\n if (\n line.startsWith(\"//\") ||\n line.startsWith(\"*\") ||\n line.startsWith(\"/*\") ||\n line.includes(\"lssi-ignore\") ||\n line.includes(\"<script\") ||\n line.includes(\"data-src\") ||\n line.includes(\"type=\\\"text/plain\\\"\") ||\n line.includes(\"type='text/plain'\")\n ) {\n continue;\n }\n\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n if (line.includes(tracker.domain)) {\n const isBlocked =\n line.includes(\"ConsentGate\") ||\n line.includes(\"consentGate\") ||\n line.includes(\"useConsent\") ||\n line.includes(\"useConsentService\") ||\n line.includes(\"data-consent\") ||\n line.includes(\"text/plain\");\n\n if (!isBlocked) {\n findings.push({\n id: \"SRC_UNBLOCKED_TRACKER\",\n rule: \"LSSI-SCRIPT-ISOLATION-001\",\n severity: \"CRITICAL\",\n category: \"SOURCE_CODE\",\n message: `Referencia a rastreador sin control de consentimiento detectada (${tracker.name}): '${tracker.domain}'`,\n file: path.relative(process.cwd(), filePath),\n line: i + 1,\n details: line.trim(),\n remediation: `Envuelve la invocación en un bloque condicional 'useConsent(\"${tracker.category}\")' o componente <ConsentGate>.`,\n });\n }\n }\n }\n }\n } catch {}\n }\n\n return count;\n }\n\n /**\n * Audit live URL by fetching HTML and verifying pre-consent isolation.\n */\n static async auditLiveUrl(url: string, findings: AuditFinding[]): Promise<void> {\n try {\n const res = await fetch(url, { headers: { \"User-Agent\": \"Solvenza-LSSI-Auditor/1.5.3\" } });\n if (!res.ok) {\n findings.push({\n id: \"URL_FETCH_FAILED\",\n rule: \"LSSI-URL-001\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `No se pudo conectar a la URL '${url}' (HTTP ${res.status}).`,\n });\n return;\n }\n\n const html = await res.text();\n\n // Check for raw unblocked tracker scripts in live HTML\n for (const tracker of KNOWN_TRACKER_PATTERNS) {\n const regex = new RegExp(`<script[^>]*src=[\"'][^\"']*${tracker.domain.replace(\".\", \"\\\\.\")}[^\"']*[\"'][^>]*>`, \"gi\");\n const matches = html.match(regex);\n if (matches) {\n for (const match of matches) {\n if (!match.includes('type=\"text/plain\"') && !match.includes(\"data-consent\")) {\n findings.push({\n id: \"URL_RAW_TRACKER_LEAK\",\n rule: \"LSSI-LEAK-001\",\n severity: \"CRITICAL\",\n category: \"LIVE_URL\",\n message: `Fuga de rastreador en tiempo de ejecución: script '${tracker.name}' cargado antes del consentimiento.`,\n details: match,\n remediation: `Asegúrate de que '${tracker.name}' se inyecte con type=\"text/plain\" data-consent=\"...\".`,\n });\n }\n }\n }\n }\n } catch (err: any) {\n findings.push({\n id: \"URL_CONNECT_ERROR\",\n rule: \"LSSI-URL-002\",\n severity: \"WARNING\",\n category: \"LIVE_URL\",\n message: `Error al escanear la URL '${url}': ${err.message}`,\n });\n }\n }\n\n /**\n * Generate GitHub Actions markdown step summary.\n */\n static formatGitHubSummary(report: AuditReport): string {\n const statusEmoji = report.compliant ? \"✅\" : \"❌\";\n const statusText = report.compliant\n ? \"**APROBADO (Cumplimiento LSSI / AEPD / RGPD Correcto)**\"\n : \"**RECHAZADO (Violaciones Críticas de Privacidad Detectadas)**\";\n\n let md = `## ${statusEmoji} Auditoría de Cumplimiento LSSI & AEPD - Solvenza Cookies Compliance\\n\\n`;\n md += `> **Resultado:** ${statusText} \\n`;\n md += `> **Puntuación de Cumplimiento:** **${report.score} / 100** \\n`;\n md += `> **Archivos Escaneados:** ${report.scannedFilesCount} | **Fecha:** ${new Date(report.timestamp).toUTCString()}\\n\\n`;\n\n md += `### 📊 Resumen de Hallazgos:\\n\\n`;\n md += `| Gravedad | Cantidad | Descripción |\\n`;\n md += `|---|---|---|\\n`;\n md += `| 🔴 **CRÍTICO** | **${report.summary.critical}** | Violaciones legales directas (riesgo de sanción AEPD) |\\n`;\n md += `| 🟡 **ADVERTENCIA** | **${report.summary.warnings}** | Recomendaciones técnicas y mejores prácticas |\\n`;\n md += `| 🔵 **INFO** | **${report.summary.info}** | Sugerencias de optimización |\\n\\n`;\n\n if (report.findings.length > 0) {\n md += `### 🔍 Detalle de Hallazgos:\\n\\n`;\n md += `| Regla | Gravedad | Categoría | Archivo/Línea | Mensaje | Solución |\\n`;\n md += `|---|---|---|---|---|---|\\n`;\n\n for (const f of report.findings) {\n const sevBadge = f.severity === \"CRITICAL\" ? \"🔴 CRÍTICO\" : f.severity === \"WARNING\" ? \"🟡 ADVERTENCIA\" : \"🔵 INFO\";\n const location = f.file ? `\\`${f.file}${f.line ? `:${f.line}` : \"\"}\\`` : \"-\";\n const remediation = f.remediation ? f.remediation.replace(/\\|/g, \"\\\\|\") : \"-\";\n const message = f.message.replace(/\\|/g, \"\\\\|\");\n md += `| \\`${f.rule}\\` | ${sevBadge} | ${f.category} | ${location} | ${message} | ${remediation} |\\n`;\n }\n md += `\\n`;\n } else {\n md += `🎉 **No se encontraron violaciones ni fugas de privacidad.** Todos los recursos de terceros cumplen con el aislamiento previo al consentimiento (LSSI art. 22.2).\\n\\n`;\n }\n\n return md;\n }\n\n private static walkDir(dir: string): string[] {\n let results: string[] = [];\n try {\n const list = fs.readdirSync(dir);\n for (const file of list) {\n const fullPath = path.join(dir, file);\n const stat = fs.statSync(fullPath);\n if (stat && stat.isDirectory()) {\n results = results.concat(this.walkDir(fullPath));\n } else {\n results.push(fullPath);\n }\n }\n } catch {}\n return results;\n }\n}\n"],"mappings":"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","names":["_a"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solvenza/cookies-compliance",
3
- "version": "1.5.1",
3
+ "version": "1.5.3",
4
4
  "description": "SDK minimalista, local-first y sin dependencias de gestión de consentimiento para España (LSSI art 22.2, AEPD, RGPD y LOPDGDD)",
5
5
  "type": "module",
6
6
  "main": "./dist/consent.esm.js",