Skip to Content
Seguridad y privacidad

Seguridad y privacidad

<mapvx-website> se ejecuta dentro de tu página, con el origen y los permisos de tu página. Además de renderizar el directorio, cargarlo tiene efectos que tu Content Security Policy (CSP), tu aviso de privacidad y tu flujo de consentimiento deben tener en cuenta:

  • El navegador del visitante se conecta a la API de MapVX y a hosts de teselas del mapa.
  • Apenas se carga main.js, el bundle carga Google Analytics 4 para la propiedad propia de MapVX, que desde ese momento recibe eventos de tu página.
  • El SDK de mapas incluido en el bundle reporta sus errores a MapVX.
  • El navegador pide acceso a la ubicación (y, en iOS, al movimiento) solo cuando el visitante usa una función de ubicación.
  • El componente guarda una preferencia en localStorage.

Esta página describe la versión publicada 1.6.1.

Content Security Policy

Si tu sitio envía un encabezado Content-Security-Policy, este debe permitir lo que el componente carga en tiempo de ejecución:

DirectivaLo que necesita el componente
script-srcmain.js, un script de módulo, desde tu origen o una CDN, y el cargador de Google Analytics que el bundle agrega a <head> (https://www.googletagmanager.com).
style-srcstyles.css, más los elementos <style> que el componente inyecta en <head> en tiempo de ejecución. Permítelos con un nonce o con 'unsafe-inline'.
style-src-attr'unsafe-inline': algunos íconos de Font Awesome, el marcador de ubicación actual y el formato del texto de Deals + Events usan atributos style en línea, y los nonces no se aplican a los atributos.
img-srcdata: (íconos incrustados en styles.css), blob: (lo usa la biblioteca del mapa en algunos navegadores) y los hosts que sirven las imágenes de los lugares.
connect-srcLa API de MapVX, las teselas, los sprites y los glifos de las etiquetas del mapa, los reportes de errores y los hits de Google Analytics.
worker-srcblob:: el worker del mapa viene incrustado en main.js y se inicia desde una URL blob:.
font-srcNada: el componente no carga fuentes web. Las etiquetas del mapa se descargan mediante connect-src.

Sin worker-src blob: (o child-src blob:), el mapa queda vacío y la única señal es una violación de CSP en la consola del navegador. No existe un archivo de worker separado que puedas alojar tú mismo en su lugar.

La sección Deals + Events del perfil de un lugar muestra texto con formato (HTML) que proviene de tu contenido en MapVX. Sin 'unsafe-inline' en script-src, los manejadores de eventos en línea de ese texto, como los atributos onclick, no se ejecutan.

Las dos políticas siguientes se probaron con el bundle publicado 1.6.1 en Chromium, en la vista de mapa con el seguimiento de la ubicación activado y en el perfil de un lugar con Deals + Events y una ruta desde la ubicación actual, y no produjeron violaciones. Para la referencia general de CSP, consulta MDN .

Política con lista de permitidos

Esta política es para un bundle alojado por ti (main.js y styles.css servidos desde tu propio origen; consulta Alojamiento propio), con un nonce para los estilos inyectados. El ejemplo arma el valor del encabezado en Node.js; las directivas son las mismas en cualquier servidor.

import { randomBytes } from 'node:crypto' // Un nonce nuevo para cada respuesta const nonce = randomBytes(16).toString('base64') const directives = { 'default-src': ["'self'"], 'script-src': ["'self'", 'https://www.googletagmanager.com'], 'style-src': ["'self'", `'nonce-${nonce}'`], 'style-src-attr': ["'unsafe-inline'"], 'img-src': ["'self'", 'data:', 'blob:', 'https:'], 'connect-src': [ "'self'", // API de MapVX 'https://public-api.mapvx.com', 'https://api.mapvx.com', // Teselas, sprites y glifos de las etiquetas del mapa 'https://tiles.mapvx.com', 'https://indoorequals.mapvx.com', 'https://api.maptiler.com', 'https://lazarillo.app', // Reportes de errores 'https://sentry.mapvx.app', // Google Analytics 'https://*.google-analytics.com', 'https://*.analytics.google.com', 'https://*.googletagmanager.com', // Decodificadores de modelos 3D (solo mapas 3D) 'https://mapvx-glb-assets.s3.us-east-1.amazonaws.com', 'https://www.gstatic.com', ], 'worker-src': ["'self'", 'blob:'], // Para tus propias fuentes: el componente no carga ninguna 'font-src': ["'self'", 'data:'], 'object-src': ["'none'"], 'base-uri': ["'self'"], } const csp = Object.entries(directives) .map(([name, sources]) => `${name} ${sources.join(' ')}`) .join('; ') // Envíalo con la respuesta HTML: // res.setHeader('Content-Security-Policy', csp)

El HTML de esa misma respuesta lleva el mismo nonce (NONCE_VALUE en el ejemplo):

<head> <link rel="stylesheet" href="/mapvx-website/1.6.1/styles.css" /> <style nonce="NONCE_VALUE"> mapvx-website { display: block; width: 100%; } </style> </head> <body> <!-- Angular pone este nonce en los elementos <style> que inyecta --> <mapvx-website ngCspNonce="NONCE_VALUE" api-key="YOUR_API_KEY" institution-id="YOUR_INSTITUTION_ID" ></mapvx-website> <script type="module" src="/mapvx-website/1.6.1/main.js"></script> </body>
  • Carga desde una CDN: agrega su origen, https://cdn.jsdelivr.net o https://unpkg.com, tanto a script-src como a style-src, y fija la versión exacta que probaste (consulta CDN).
  • Los hosts del mapa dependen de los datos: el estilo de mapa que la API devuelve para cada institución indica los hosts de teselas, sprites y glifos. La lista cubre los hosts vistos en los estilos de mapa de MapVX, más indoorequals.mapvx.com, un host de teselas de interiores de MapVX al que las pruebas no llegaron. Si la consola informa que se bloqueó una solicitud del mapa, agrega ese host.
  • Las imágenes de los lugares vienen de los hosts que usa el contenido de tu institución; la imagen de reemplazo para los lugares sin imagen está en images.ctfassets.net. Por eso img-src permite https:. Restríngelo solo si conoces todos los hosts de imágenes.
  • Decodificadores 3D: los dos últimos hosts de connect-src sirven decodificadores de modelos 3D, que el SDK carga solo para mapas 3D. El mapa del componente es 2D, así que no se contactan en el uso normal; formaron parte de la política probada.
  • Agrega los orígenes de tu propio sitio (scripts, fuentes, analítica) a las mismas directivas.

Nonce para los estilos inyectados

En tiempo de ejecución, Angular agrega a <head> elementos <style> con los estilos del componente, y Font Awesome agrega uno más (<style id="fa-auto-css">). Para permitirlos sin 'unsafe-inline', entrégale a Angular un nonce con el atributo ngCspNonce:

  1. Genera un nonce aleatorio nuevo para cada respuesta y agrégalo a style-src como 'nonce-…'.
  2. Pon ngCspNonce con el mismo valor en <mapvx-website>, o en cualquier otro elemento dentro de <body> (no en el propio <body>), en el HTML que envía tu servidor, para que exista antes de que se ejecute main.js. Angular usa el primer elemento que tenga el atributo.

Si tu framework crea <mapvx-website> en el navegador, pon el atributo en otro elemento que sí esté en el HTML del servidor, por ejemplo <div ngCspNonce="NONCE_VALUE" hidden></div>.

El nonce cubre todos los elementos <style> que Angular inyecta para el componente y el fa-auto-css de Font Awesome. No cubre:

  • El cargador de Google Analytics. El bundle agrega ese <script> sin nonce, y no hay ninguna opción para pasarle uno. Permite https://www.googletagmanager.com en script-src, o usa 'strict-dynamic'.
  • Los atributos style en línea. Necesitan style-src-attr 'unsafe-inline'. Los navegadores ignoran 'unsafe-inline' en una directiva que también contiene un nonce; por eso los atributos tienen su propia directiva.
  • styles.css. Es una hoja de estilos normal, permitida por 'self' o por el origen de la CDN.

Si no puedes generar un nonce por respuesta, por ejemplo en un alojamiento totalmente estático, usa style-src 'self' 'unsafe-inline' y omite style-src-attr, que en ese caso toma el valor de style-src. Sin nonce y sin 'unsafe-inline', el navegador bloquea los elementos <style> inyectados y el componente se renderiza sin sus estilos de diseño.

Alternativa con 'strict-dynamic'

Para una política de scripts basada en nonces, usa un nonce y 'strict-dynamic' en script-src, y pon el nonce en la etiqueta de main.js. Así, los scripts que agrega el propio bundle, incluido el cargador de Google Analytics, quedan permitidos sin listar sus hosts. Las demás directivas quedan como en la política con lista de permitidos; esta variante tampoco produjo violaciones.

// Reemplaza script-src de la política con lista de permitidos, antes de armar el encabezado directives['script-src'] = [`'nonce-${nonce}'`, "'strict-dynamic'"]
<script type="module" nonce="NONCE_VALUE" src="/mapvx-website/1.6.1/main.js"></script>
  • El mismo valor de nonce puede servir para script-src y style-src.
  • Con 'strict-dynamic', los navegadores ignoran los hosts y 'self' en script-src. Lo que permite main.js es el nonce de la etiqueta, sin importar desde dónde se sirva, y en este modo una lista de hosts no puede dejar fuera al cargador de Google Analytics.
  • styles.css sigue necesitando su origen en style-src cuando viene de una CDN.

Servicios de terceros

Estos son los hosts que el navegador del visitante contacta a causa del componente:

HostPropósitoCuándo
public-api.mapvx.comAPI de MapVX: lugares, configuración, instituciones, búsqueda, detalle de lugares y estilo del mapaEn cuanto se define api-key
api.mapvx.comAPI de MapVX: indicaciones a pieCuando el visitante pide una ruta
tiles.mapvx.com, api.maptiler.com y otros hosts que indica el estilo del mapaTeselas, sprites y glifos de las etiquetas del mapaMientras se muestra un mapa
Los hosts de imágenes de tu institución, images.ctfassets.netImágenes de los lugares, y la imagen de reemplazo para los lugares sin imagenCuando se muestran tarjetas y perfiles
www.googletagmanager.com, *.google-analytics.com, *.analytics.google.comGoogle Analytics 4, la propiedad propia de MapVXApenas se carga main.js, incluso sin api-key
sentry.mapvx.appReportes de errores a MapVXSolo cuando se captura un error
www.google.comGoogle Maps, que Driving directions abre en una pestaña nuevaSolo cuando el visitante hace clic en ese enlace

Si cargas el bundle desde unpkg o jsDelivr, el navegador también contacta esa CDN.

Lo que recibe la API de MapVX. Tu API key (en el encabezado Authorization), los términos que busca el visitante, los ids de los lugares que abre y, cuando el visitante elige Use current location como punto de partida de una ruta, sus coordenadas, que la API necesita para calcular la ruta. Como cualquier solicitud de un navegador, estas solicitudes también llevan la dirección IP del visitante y los encabezados estándar del navegador. El componente no hace ninguna solicitud a la API de MapVX hasta que se define api-key.

La API key es visible. Está escrita en el HTML de tu página como atributo api-key, así que cualquiera puede leerla.

Google Analytics

El bundle incluye Google Analytics 4 y envía eventos a la propiedad GA4 propia de MapVX, en todas las páginas que cargan main.js:

  • Apenas se evalúa main.js, antes de que exista cualquier <mapvx-website> e incluso sin api-key, reutiliza window.dataLayer y window.gtag si tu página ya los define (o los crea), encola el comando config de MapVX con la vista de página inicial desactivada y agrega a <head> el cargador gtag.js desde https://www.googletagmanager.com, sin nonce.
  • Cada evento del componente se envía con send_to apuntando al ID de medición de MapVX, así que llega solo a la propiedad de MapVX, no a la tuya.
  • No hay ningún atributo ni API para desactivarlo.

Qué contienen los eventos. Todos los eventos llevan estos parámetros:

ParámetroContenido
hostEl nombre de host de tu página (location.hostname).
sessionIdUn id aleatorio generado en el navegador. No se guarda: se crea uno nuevo en cada carga de página y cuando se quita el elemento.
apiKeyEl valor de api-key, que identifica tu integración.
institutionIdLa institución seleccionada (desde institution-id, o la ciudad que elige el visitante).
parentPlaceIdEl edificio que se muestra en el mapa, si lo hay.

Los eventos describen lo que el visitante hace en el componente: el inicio y el fin de la sesión (con su duración), las pantallas que ve (como rutas internas, por ejemplo /list?view=grid o /profile/<id>), el cambio entre cuadrícula y mapa, los filtros de categoría, los términos de búsqueda y la cantidad de resultados, los ids, nombres y alias de los lugares que abre o selecciona (tarjetas, marcadores, origen y destino de una ruta), las URL de los sitios web de los lugares y de los enlaces de indicaciones para conducir en los que hace clic, y los clics en los controles del mapa y de la ruta. Las coordenadas del visitante nunca se incluyen: el botón de ubicación registra solo el clic, y una ruta desde la ubicación actual se registra como current-location.

Además de estos parámetros, gtag.js envía los datos que Google Analytics recopila por defecto en cada hit, como la URL de tu página, y crea sus cookies propias (consulta Almacenamiento en el navegador).

Actividad en tu página. El comando config de MapVX se aplica a toda la página, no solo al componente, así que la propiedad de MapVX también recibe:

  • los eventos que tu propio código envía con gtag('event', …) sin send_to;
  • los eventos que Google Analytics mide automáticamente según la configuración de la propiedad de MapVX. En una prueba de septiembre de 2026, fueron el desplazamiento por la página, los clics en enlaces a otros sitios, las vistas de página cuando tu página cambia su URL mediante la History API y los términos de búsqueda que se leen de la query string de tu página (como ?q=).

Estos eventos automáticos dependen de la configuración de la propiedad de Google Analytics de MapVX, no del bundle, así que pueden cambiar sin una nueva versión del componente.

Consentimiento. Considera este tratamiento en tu aviso de privacidad y en tu flujo de consentimiento. El componente no tiene un interruptor propio; las opciones son:

  • Cargar main.js solo después de que el visitante dé su consentimiento. La configuración de Google Analytics se ejecuta cuando se evalúa el bundle, así que el componente tampoco puede renderizarse antes de ese momento.
  • Definir los valores por defecto de Google Consent Mode antes de que se cargue main.js. La etiqueta de MapVX usa el dataLayer de tu página, así que los respeta. En una prueba con analytics_storage en 'denied', gtag.js no creó cookies, pero siguió enviando hits sin ellas.
  • Bloquear https://www.googletagmanager.com en script-src (política con lista de permitidos). gtag.js no se carga y el componente sigue funcionando. CSP ignora la query string al comparar orígenes, así que no puedes permitir tu propio gtag.js desde ese host y bloquear el de MapVX: si omites el host, bloqueas ambos. Con 'strict-dynamic', una lista de hosts no puede bloquearlo.

Si tu página usa sus propias etiquetas de Google, revisa el resultado con tus herramientas de depuración de etiquetas.

Reporte de errores

En 1.6.1, los reportes de errores provienen del SDK de mapas incluido en el bundle: este envía sus propios errores, por ejemplo una solicitud fallida a la API de MapVX, al seguimiento de errores de MapVX en https://sentry.mapvx.app. Los errores que produce el componente fuera del SDK solo se escriben en la consola del navegador.

  • El cliente de reportes se crea cuando se inicializa el SDK, una vez definido api-key, y envía un reporte solo cuando captura un error.
  • Es un cliente de Sentry aislado: no llama a Sentry.init() y omite las integraciones que se enganchan al estado global (manejadores globales de errores, envoltorios de temporizadores y de listeners de eventos, breadcrumbs automáticos de console, DOM, fetch, XHR e historial, y sesiones). No toca window.onerror, window.onunhandledrejection ni tu propia configuración de Sentry. Una integración por defecto modifica Function.prototype.toString en toda la página; la modificación devuelve el resultado original para las funciones que no son de Sentry.
  • Un reporte lleva el mensaje de error y el stack trace, la URL completa de tu página (incluida la query string), el User-Agent y el Referer, el idioma regional y la zona horaria del navegador, la versión del SDK y un rastro de las llamadas al SDK hechas antes del error, con los ids de algunos de los lugares involucrados.
  • No hay ningún atributo para desactivarlo. Bloquear https://sentry.mapvx.app en connect-src detiene los reportes; en ese caso el navegador registra una violación de CSP por cada reporte bloqueado.

El alcance del reporte de errores puede cambiar entre versiones: revisa Novedades antes de actualizar.

Permisos del navegador

El componente no pide ningún permiso al cargar. Al renderizarse, solo lee el estado del permiso de geolocalización, lo que no muestra ninguna solicitud.

PermisoCuándo lo pide el navegadorPara qué se usa
GeolocalizaciónCuando el visitante hace clic en el botón de ubicación del mapa, o elige Use current location como punto de partida de una ruta.Mostrar y seguir la posición del visitante; el punto de partida de una ruta.
Movimiento y orientaciónEn los navegadores que exigen un permiso explícito (Safari en iOS), cuando empieza el seguimiento de la ubicación.Girar el marcador de ubicación según el rumbo de la brújula.
  • Si el visitante niega el acceso a la ubicación, el botón de ubicación no hace nada y Use current location se oculta. El resto del componente sigue funcionando.
  • Una vez activado el seguimiento, la posición se observa de forma continua, con alta precisión, mientras se muestra un mapa. El seguimiento se pausa cuando el mapa sale de la pantalla y se reanuda cuando se vuelve a mostrar un mapa.
  • Las posiciones se quedan en el navegador, salvo el punto de partida de una ruta, que se envía a la API de MapVX como se describe en Servicios de terceros.
  • La geolocalización requiere un contexto seguro (HTTPS). Si tu página está incrustada en un <iframe> de otro origen, el iframe necesita allow="geolocation"; un iframe del mismo origen tiene acceso por defecto. Si tu sitio envía un encabezado Permissions-Policy, este no debe desactivar geolocation.
  • El componente no pide ningún otro permiso (cámara, micrófono, notificaciones…).

Almacenamiento en el navegador

AlmacenamientoClaveContenidoLo escribe
localStoragemapvx:active-viewgrid o map: la última elección del visitante entre Grid View y Map View.El componente, cuando el visitante usa el selector.
Cookies_ga y relacionadasIdentificadores de Google Analytics.gtag.js, como en cualquier instalación de Google Analytics.
  • mapvx:active-view se aplica a todo el origen: todas las páginas y todos los <mapvx-website> de tu sitio la comparten, y reemplaza a default-to-map en las visitas siguientes (consulta Vista recordada).
  • Si el almacenamiento no está disponible, por ejemplo porque el navegador bloquea los datos del sitio, el componente ignora el error y usa default-to-map.
  • El componente no crea cookies por sí mismo. Ni él ni el SDK incluido guardan datos en sessionStorage, IndexedDB ni la Cache API, y ninguno registra un service worker; las cachés del SDK viven solo en memoria.
  • El id de sesión de la analítica no se guarda.

Si un problema de políticas o permisos se manifiesta como un síntoma visible, consulta Solución de problemas.

Última actualización: