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:
| Directiva | Lo que necesita el componente |
|---|---|
script-src | main.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-src | styles.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-src | data: (í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-src | La 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-src | blob:: el worker del mapa viene incrustado en main.js y se inicia desde una URL blob:. |
font-src | Nada: 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.netohttps://unpkg.com, tanto ascript-srccomo astyle-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 esoimg-srcpermitehttps:. Restríngelo solo si conoces todos los hosts de imágenes. - Decodificadores 3D: los dos últimos hosts de
connect-srcsirven 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:
- Genera un nonce aleatorio nuevo para cada respuesta y agrégalo a
style-srccomo'nonce-…'. - Pon
ngCspNoncecon 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 ejecutemain.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. Permitehttps://www.googletagmanager.comenscript-src, o usa'strict-dynamic'. - Los atributos
styleen línea. Necesitanstyle-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-srcystyle-src. - Con
'strict-dynamic', los navegadores ignoran los hosts y'self'enscript-src. Lo que permitemain.jses 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.csssigue necesitando su origen enstyle-srccuando viene de una CDN.
Servicios de terceros
Estos son los hosts que el navegador del visitante contacta a causa del componente:
| Host | Propósito | Cuándo |
|---|---|---|
public-api.mapvx.com | API de MapVX: lugares, configuración, instituciones, búsqueda, detalle de lugares y estilo del mapa | En cuanto se define api-key |
api.mapvx.com | API de MapVX: indicaciones a pie | Cuando el visitante pide una ruta |
tiles.mapvx.com, api.maptiler.com y otros hosts que indica el estilo del mapa | Teselas, sprites y glifos de las etiquetas del mapa | Mientras se muestra un mapa |
Los hosts de imágenes de tu institución, images.ctfassets.net | Imágenes de los lugares, y la imagen de reemplazo para los lugares sin imagen | Cuando se muestran tarjetas y perfiles |
www.googletagmanager.com, *.google-analytics.com, *.analytics.google.com | Google Analytics 4, la propiedad propia de MapVX | Apenas se carga main.js, incluso sin api-key |
sentry.mapvx.app | Reportes de errores a MapVX | Solo cuando se captura un error |
www.google.com | Google Maps, que Driving directions abre en una pestaña nueva | Solo 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 sinapi-key, reutilizawindow.dataLayerywindow.gtagsi tu página ya los define (o los crea), encola el comandoconfigde MapVX con la vista de página inicial desactivada y agrega a<head>el cargadorgtag.jsdesdehttps://www.googletagmanager.com, sin nonce. - Cada evento del componente se envía con
send_toapuntando 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ámetro | Contenido |
|---|---|
host | El nombre de host de tu página (location.hostname). |
sessionId | Un 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. |
apiKey | El valor de api-key, que identifica tu integración. |
institutionId | La institución seleccionada (desde institution-id, o la ciudad que elige el visitante). |
parentPlaceId | El 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', …)sinsend_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.jssolo 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 eldataLayerde tu página, así que los respeta. En una prueba conanalytics_storageen'denied',gtag.jsno creó cookies, pero siguió enviando hits sin ellas. - Bloquear
https://www.googletagmanager.comenscript-src(política con lista de permitidos).gtag.jsno se carga y el componente sigue funcionando. CSP ignora la query string al comparar orígenes, así que no puedes permitir tu propiogtag.jsdesde 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 deconsole, DOM,fetch, XHR e historial, y sesiones). No tocawindow.onerror,window.onunhandledrejectionni tu propia configuración de Sentry. Una integración por defecto modificaFunction.prototype.toStringen 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-Agenty elReferer, 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.appenconnect-srcdetiene 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.
| Permiso | Cuándo lo pide el navegador | Para qué se usa |
|---|---|---|
| Geolocalización | Cuando 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ón | En 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 necesitaallow="geolocation"; un iframe del mismo origen tiene acceso por defecto. Si tu sitio envía un encabezadoPermissions-Policy, este no debe desactivargeolocation. - El componente no pide ningún otro permiso (cámara, micrófono, notificaciones…).
Almacenamiento en el navegador
| Almacenamiento | Clave | Contenido | Lo escribe |
|---|---|---|---|
localStorage | mapvx:active-view | grid 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 relacionadas | Identificadores de Google Analytics. | gtag.js, como en cualquier instalación de Google Analytics. |
mapvx:active-viewse aplica a todo el origen: todas las páginas y todos los<mapvx-website>de tu sitio la comparten, y reemplaza adefault-to-mapen 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.