Protección de la cadena de suministro

Un agente de programación autónomo instala paquetes constantemente: preparar la estructura de un proyecto arrastra cientos de dependencias sin que ninguna persona llegue a leer un nombre, y mucho menos un registro de cambios. Eso convierte al registro de paquetes en el canal más amplio para que código hostil entre en el sandbox: un nombre con typosquatting, una versión recién secuestrada o un script postinstall malicioso se ejecuta con todos los permisos del agente en el mismo momento en que npm install termina. Bromure Agentic Coding trata cada descarga de paquete como entrada no confiable y le aplica una política por espacio de trabajo en el anfitrión, antes de que un solo byte llegue a la VM.

Este capítulo explica qué se intercepta, cada comprobación de la canalización, qué ocurre cuando una comprobación se activa y cómo leer los resultados en el Registro de seguridad. La referencia campo por campo de los ajustes del panel se encuentra en Ajustes de cadena de suministro.

Por qué las instalaciones de paquetes son la superficie de ataque

El sandbox ya contiene al agente: no puede tocar tu Mac, y sus credenciales son señuelos (consulta Credenciales). Lo que el sandbox no puede hacer por sí solo es juzgar la procedencia del código que el agente incorpora. Los ataques a la cadena de suministro explotan precisamente esa brecha:

  • Secuestros de versiones recientes. La cuenta de un mantenedor se ve comprometida y se publica una versión maliciosa. Estas versiones suelen detectarse y retirarse en cuestión de horas o días, razón por la cual el control de antigüedad rechaza las versiones más recientes que un umbral.
  • Versiones con vulnerabilidades conocidas. El agente resuelve una dependencia a una versión con un aviso publicado. La comprobación OSV las detecta.
  • Malware, typosquats y scripts de instalación maliciosos. Paquetes creados para ser maliciosos desde el principio. Los proveedores socket.dev y Delpi los marcan o filtran, y la eliminación de scripts de instalación suprime de raíz el vector de ejecución más habitual.

La aplicación de la política es del lado del anfitrión por diseño. El proxy aplica la política a la respuesta antes de que el agente la vea, de modo que nada que se ejecute dentro de la VM —incluido un agente totalmente comprometido— puede relajar las reglas. El .npmrc y el pip.conf internos de la VM solo pueden restringir aún más lo que el proxy ya sirvió, nunca ampliarlo. Las claves de API de los servicios de reputación (socket.dev, Delpi) se conservan únicamente en el anfitrión y nunca se exportan a la VM.

Cómo funciona la interceptación

Cada petición de red que hace la VM pasa por el proxy MITM del lado del anfitrión (consulta Conceptos). El proxy reconoce las peticiones a los principales registros de paquetes y clasifica cada una:

  • Metadatos — el listado de versiones de un paquete (un packument de npm, la API JSON de PyPI o el índice /simple/, una entrada del índice disperso de Cargo, etc.). El control de antigüedad opera aquí, reescribiendo el listado.
  • Artefacto — el archivo descargable de una versión concreta: un .tgz de npm, una wheel o sdist de Python, un .crate, .gem, .nupkg o el .zip de un módulo de Go. Las consultas a OSV, las comprobaciones de socket.dev, la eliminación de scripts y los bloqueos duros ocurren todos en el momento de la descarga del artefacto.
  • Paso directo — todo lo demás en esos anfitriones (búsqueda, autenticación). Sin modificar.

La interceptación es automática en cuanto al menos una capa de cadena de suministro está habilitada para el espacio de trabajo (o el espacio de trabajo está inscrito en bromure.io, en cuyo caso las descargas se observan para telemetría aunque todas las capas de aplicación estén desactivadas). No hay nada que instalar ni configurar dentro de la VM.

Nota: Los paquetes que ya están presentes en la caché local de npm o de pip nunca llegan a la red, así que no se comprueba —ni se registra— nada sobre ellos. Un Registro de seguridad silencioso durante una instalación puede significar simplemente que todo provino de la caché.

Veredictos: permitir, reescribir, bloquear, retener

Las capas de cadena de suministro son interruptores individuales de activación/desactivación, cada uno con una acción fija: no hay un modo global de bloquear/advertir/permitir para este panel. (El selector de tres opciones registrar/preguntar/bloquear que quizá conozcas de Inyección de prompts, y los modos de aplicación de Salvaguardas, son sistemas distintos.) Cada descarga termina en uno de cuatro resultados:

ResultadoQué ocurreMarcador visible
PermitidoLa respuesta pasa sin modificar.Marca verde en el Registro de seguridad; una línea de «inspección» confirma que el proxy la vio.
ReescritoEl proxy modificó la respuesta: versiones demasiado recientes eliminadas de los metadatos, o scripts de instalación eliminados de un tarball. El gestor de paquetes continúa con normalidad.Cabecera de respuesta X-Bromure-Rewritten: supply-chain; líneas naranjas de «eliminado» en el registro.
BloqueadoLa descarga se rechaza con una respuesta HTTP 451 («No disponible por razones legales»).Cabecera X-Bromure-Block: supply-chain; línea roja en el registro.
Retenido para consentimientoLa descarga se pausa mientras Bromure te pregunta: para los pasos directos fijados por lockfile y para paquetes que ninguna fuente de reputación habilitada pudo examinar. Denegar convierte la retención en un bloqueo 451.Una alerta de consentimiento del sistema; la decisión se registra.

Un bloqueo 451 lleva un cuerpo en texto plano que comienza con Bromure Supply-Chain Security blocked this request: seguido del motivo exacto, por ejemplo:

Bromure Supply-Chain Security blocked this request: npm package [email protected]
published 4 hours ago — policy requires 2 days minimum

npm, pip, cargo y los demás gestores de paquetes imprimen ese cuerpo textualmente en su salida de error, de modo que tanto el agente como tú veis con precisión por qué falló una instalación, y el agente a menudo puede sortearlo por su cuenta (por ejemplo, fijando una versión más antigua). El estado 451 se eligió deliberadamente para que los bloqueos de cadena de suministro se distingan de un vistazo de los 403 que usan las Salvaguardas.

Cobertura por ecosistema

El proxy intercepta ocho ecosistemas de paquetes. No todas las comprobaciones admiten todos los ecosistemas:

EcosistemaAnfitriones interceptadosControl de antigüedadOSVsocket.devDelpiEliminación de scripts
npmregistry.npmjs.org, *.npmjs.org
PyPIpypi.org, files.pythonhosted.org
Cargocrates.io, static.crates.io, index.crates.ioNo
RubyGemsrubygems.org
Maven Centralrepo1.maven.org, repo.maven.apache.org, search.maven.orgNo
NuGetapi.nuget.org, *.nuget.orgNo
Módulos de Goproxy.golang.orgNo
Packagistrepo.packagist.org, packagist.org

Dos lagunas que conviene conocer:

  • Control de antigüedad — Maven, NuGet, Go. Estos tres ecosistemas no incluyen marcas de tiempo de publicación por versión en sus respuestas de metadatos estándar, así que sus metadatos pasan sin filtrar y el respaldo a nivel de artefacto no tiene datos con los que actuar. El control de antigüedad hoy en día no los bloquea de forma efectiva; npm, PyPI, Cargo, RubyGems y Packagist están totalmente cubiertos.
  • socket.dev — Cargo. socket.dev no admite Cargo. Con el filtrado de socket.dev habilitado, cada artefacto de crates.io no produce veredicto y, por lo tanto, activa el aviso de consentimiento de paquete no verificado (consulta Comportamiento sin conexión y degradado). Si tu espacio de trabajo hace mucho trabajo en Rust, o bien respondes a los avisos o eliges otro proveedor.

El control de antigüedad

El control de antigüedad rechaza las versiones de paquete más recientes que un número configurable de días, partiendo de la premisa de que una versión recién publicada es la que tiene más probabilidades de ser un paquete recién secuestrado: las versiones maliciosas suelen reportarse y retirarse rápidamente, y esperar a que pase esa ventana no cuesta casi nada para el desarrollo habitual. Es la única capa habilitada de forma predeterminada, con un mínimo de 2 días.

El panel Cadena de suministro de la ventana Editar espacio de trabajo, mostrando el grupo Control de antigüedad con Rechazar paquetes más recientes que el umbral marcado y Antigüedad mínima fijada en 2 días, y el grupo Comprobación de vulnerabilidades OSV sin marcar debajo

Funciona mediante dos mecanismos que colaboran:

  • Reescritura de metadatos. El proxy elimina las versiones demasiado recientes del listado de versiones del registro y reorienta latest y otras dist-tags a la versión más nueva que sobrevive. Las referencias flotantes —pkg@latest, rangos de semver— por lo tanto nunca se bloquean de forma dura: el gestor de paquetes resuelve silenciosamente a la versión más nueva que sea lo bastante antigua como para pasar. Desde el punto de vista del agente, las versiones más recientes que el umbral simplemente aún no existen.
  • Respaldo en la descarga del artefacto. Cada marca de tiempo de publicación por versión que el proxy ve en los metadatos se almacena en memoria (hasta 50.000 entradas). Si el agente solicita luego directamente una versión fijada demasiado reciente, la descarga se bloquea con un 451 que indica la antigüedad real del paquete y el mínimo requerido. Para pip —cuyo índice HTML PEP 503 predeterminado no incluye marcas de tiempo— Bromure realiza una consulta bajo demanda a https://pypi.org/pypi/<pkg>/<version>/json para obtener la fecha de publicación.

Para configurarlo, abre el panel Cadena de suministro del espacio de trabajo y usa el grupo Control de antigüedad:

  1. Activa Rechazar paquetes más recientes que el umbral.
  2. Fija Antigüedad mínima: con el selector incremental (0–90 días).
  3. Si un paquete concreto debe poder instalarse de inmediato —por ejemplo, tu propio equipo lo publica— añádelo en Paquetes exentos con el botón Añadir entrada. Las entradas usan el formato de lista de permitidos: npm:axios limita la exención a un ecosistema, mientras que un axios a secas coincide con el nombre del paquete en todos los ecosistemas. La coincidencia no distingue mayúsculas de minúsculas.

Consejo: Las peticiones de metadatos de npm se actualizan silenciosamente al packument completo para que el control pueda ver las fechas de publicación; no tienes que hacer nada para el formato de metadatos abreviado de npm.

Comprobación de vulnerabilidades OSV

La comprobación OSV busca el ecosistema, el paquete y la versión de cada artefacto descargado en api.osv.dev, la base de datos gratuita Open Source Vulnerabilities, que agrega la GitHub Advisory Database, los avisos de PyPI, la base de datos de vulnerabilidades de Go, RubySec y otras. Si algún aviso para esa versión exacta iguala o supera la gravedad que hayas elegido, la descarga se bloquea con un 451. Cubre los ocho ecosistemas y no requiere clave de API.

La gravedad se toma de la etiqueta GHSA cuando el aviso la tiene; de lo contrario, Bromure calcula la puntuación base CVSS v3 a partir de la cadena de vector del aviso.

En el grupo Comprobación de vulnerabilidades OSV:

  1. Activa Buscar paquetes en api.osv.dev (gratis, sin clave requerida).
  2. Elige Bloquear a partir de la gravedad:Baja y superior, Media y superior, Alta y superior o Solo crítica.

La comprobación está desactivada de forma predeterminada, y el umbral de gravedad tiene como valor predeterminado Alta y superior: como el propio panel señala, un CVE de baja gravedad en un subpaquete transitivo no debería interrumpir un flujo de trabajo. Las búsquedas se ejecutan solo en las descargas de artefactos (no en las de metadatos), los resultados se almacenan en memoria durante la ejecución de la app, se ejecutan como máximo 16 búsquedas en paralelo y los errores de red transitorios se reintentan hasta 5 veces con retroceso. Si no se puede alcanzar OSV en absoluto, el paquete se retiene a la espera de tu consentimiento en lugar de permitirse silenciosamente; consulta Comportamiento sin conexión y degradado.

Filtrado de paquetes: socket.dev y Delpi

El grupo Filtrado de paquetes selecciona un proveedor de reputación mediante un selector mutuamente excluyente: Ninguno, socket.dev o Delpi. Los dos proveedores funcionan de maneras fundamentalmente distintas: socket.dev es una consulta previa que el proxy realiza antes de dejar pasar una descarga; Delpi reemplaza por completo el registro de npm. Solo uno puede estar activo a la vez por diseño (ejecutar ambos filtraría dos veces cada instalación), y seleccionar Ninguno desactiva ambos mientras conserva las claves almacenadas. Sea cual sea el proveedor que elijas, las demás capas de este capítulo —control de antigüedad, OSV, eliminación de scripts— siguen aplicándose por encima.

El valor predeterminado es Ninguno. Los espacios de trabajo creados antes de que existiera el selector que ya tienen una clave de socket.dev se infieren automáticamente como socket.dev.

socket.dev

socket.dev es un servicio comercial de reputación de paquetes. Con socket.dev seleccionado y una clave de API introducida, el proxy comprueba cada descarga de artefacto contra la API de incidencias de socket.dev antes de servirla. Admite npm, PyPI, Go, Maven, RubyGems, NuGet y Packagist, pero no Cargo (consulta Cobertura por ecosistema).

Tú aportas tu propia clave: haz clic en Obtener una clave de API junto al campo Clave de API: (abre socket.dev/dashboard/settings/api-tokens), crea un token y pégalo en el campo seguro. Ambos interruptores de bloqueo permanecen deshabilitados hasta que se introduce una clave, y una clave vacía desactiva socket.dev por completo independientemente de los interruptores. La clave se almacena del lado del anfitrión en el profile.json del espacio de trabajo y nunca entra en la VM; todas las búsquedas se originan desde tu Mac.

Hay dos bloqueos independientes disponibles:

  • Bloquear paquetes comprometidos (scripts de instalación maliciosos, marcados como malware, typosquats, telemetría sospechosa) — se activa con las incidencias de riesgo de cadena de suministro con forma de ataque de socket.dev. Las señales definitivas de malware (malware, malware conocido, malware detectado por GPT, paquetes troll, claves SSH comprometidas) bloquean con cualquier gravedad. Las señales con forma de ataque más ruidosas —código ofuscado, cadenas sospechosas, scripts de instalación, typosquatting, acceso a la shell, uso inusual de HTTPS— bloquean solo cuando socket.dev las califica de altas o críticas, de modo que un paquete con un postinstall benigno no queda atrapado. Las señales puramente de calidad y de atributos (autor nuevo, lecturas de variables de entorno, acceso a la red, telemetría) deliberadamente nunca bloquean.
  • Bloquear paquetes con CVE conocidos — se activa con el cubo de vulnerabilidades de socket.dev que iguala o supera el selector Umbral de bloqueo de CVE: (los mismos cuatro niveles que OSV; predeterminado Alta y superior).

Ambos bloqueos están desactivados de forma predeterminada. Los resultados se almacenan en caché durante la ejecución de la app, se ejecutan como máximo 16 llamadas en paralelo y los fallos transitorios se reintentan 5 veces con retroceso. Los errores de autenticación se muestran en el Registro de seguridad con el estado HTTP y una vista previa del cuerpo de la respuesta.

Nota: Si habilitas OSV y el bloqueo de CVE de socket.dev a la vez, ambos comprobarán cada artefacto; eso está permitido (son capas distintas), simplemente redundante para la cobertura de CVE. Muchos usuarios combinan el bloqueo de paquetes comprometidos de socket.dev con la comprobación OSV gratuita en su lugar.

Delpi

Delpi es un registro de npm seguro y directo de Lupin & Holmes (landh.tech) que sirve paquetes examinados previamente. En lugar de buscar los paquetes, Bromure reencamina cada petición al registro de npm —metadatos, tarballs, auditoría, cualquier cosa dirigida a registry.npmjs.org o *.npmjs.org— al registro de filtrado compatible con npm de Delpi en depi-npm-proxy.landh.tech:443, adjuntando tu clave como cabecera Authorization: Bearer y eliminando cualquier cabecera Authorization que envíe el invitado. Delpi reescribe las URL de los tarballs en sus packuments para que apunten a sí mismo, de modo que esas descargas posteriores también reciben la clave inyectada del lado del anfitrión.

Para habilitarlo, selecciona Delpi en el selector y pega tu clave en el campo seguro Clave de API: (Obtener una clave de API abre landh.tech). Mientras el campo está vacío, una advertencia naranja indica Introduce una clave de API — Delpi permanece desactivado sin una. Cada petición reencaminada se registra en el Registro de seguridad:

[delpi] GET registry.npmjs.org/… → https://depi-npm-proxy.landh.tech

El manejo de errores es explícito en lugar de silencioso:

  • 401 (clave rechazada). Bromure sustituye por un error claro en texto plano que npm imprime («Bromure: the Delpi registry rejected the configured API key…», con la cabecera X-Bromure-Block: delpi-auth), lo registra y genera una alerta de interfaz gráfica única por cada combinación de espacio de trabajo y clave: «Delpi rejected your API key». Las sesiones SSH/TUI sin interfaz no reciben la alerta gráfica; dependen de la línea del registro y del error de npm reescrito.
  • 403 (paquete bloqueado por Delpi, o clave no autorizada para él). Se registra y se pasa sin cambios, de modo que npm reporta el texto de rechazo propio de Delpi.

Delpi afecta únicamente a npm; los otros siete ecosistemas no se ven afectados por él. Delpi reemplaza a socket.dev en el selector, no a tu política local: el control de antigüedad, la comprobación OSV y la eliminación de scripts siguen ejecutándose sobre lo que Delpi sirve.

Eliminación de scripts de instalación

Los hooks preinstall, install, postinstall y prepare de npm ejecutan código arbitrario en el momento de la instalación y son el caballo de batalla del malware real de npm. Con Eliminar preinstall / install / postinstall / prepare de los tarballs de npm sobre la marcha habilitado (en el grupo Scripts de instalación), el proxy reescribe cada tarball de npm en tránsito: descomprime el .tgz, localiza el package/package.json de nivel superior, elimina esas cuatro claves de script, recalcula la suma de comprobación de la cabecera tar y vuelve a comprimir con gzip.

Como los bytes del tarball cambian, el proxy también elimina dist.integrity y dist.shasum de los metadatos de registro del paquete, de modo que npm calcula el hash por sí mismo a partir del tarball ya despojado y su verificación sigue superándose para las instalaciones sin fijar. Cada eliminación se registra («eliminados los scripts de instalación de» el paquete y la versión, mostrado en naranja); los tarballs que se inspeccionaron y resultaron limpios se etiquetan solo con la cabecera X-Bromure-Rewritten: supply-chain. Ante cualquier fallo de análisis, el tarball original pasa sin modificar: esta capa falla de forma abierta, porque nunca debe inutilizar una instalación bien formada.

Algunos paquetes necesitan realmente los scripts de instalación —compiladores de enlaces nativos como better-sqlite3 o node-canvas—. Añádelos a Permitir scripts de instalación para (formato npm:better-sqlite3) y conservarán sus hooks.

El interruptor está desactivado de forma predeterminada, y se aplican dos límites:

  • Solo npm. Los sdists de PyPI no se reescriben: setup.py es código arbitrario, así que la eliminación no es viable ahí.
  • Solo instalaciones sin fijar. Un tarball cuyo hash de integridad está fijado en package-lock.json no puede reescribirse sin fallar la verificación. Ese caso lo gobierna la siguiente capa.

Instalaciones fijadas por lockfile

Una instalación fijada por lockfilenpm ci— fija el hash de integridad de cada tarball en el lockfile. Bromure no puede reescribir esos tarballs sin romper la verificación del hash, así que sus únicas opciones son dejarlos pasar sin modificar o bloquearlos. De forma predeterminada, pasan silenciosamente.

Si quieres tener voz, habilita Preguntar antes de dejar pasar sin modificar los tarballs fijados por lockfile (npm ci, pip --require-hashes) en el grupo Instalaciones fijadas por lockfile. La primera descarga fijada por lockfile de un lote (detectada para npm mediante la cabecera de petición npm-command: ci) muestra entonces un cuadro de diálogo de consentimiento del anfitrión titulado ¿Dejar pasar npm ci (instalación fijada por lockfile) del espacio de trabajo «…»? con los botones Permitir durante 15 minutos, Permitir una vez, Permitir durante el resto de la sesión y No permitir. Toda la ráfaga de descargas concurrentes se agrupa en ese único aviso y sigue tu decisión; denegar bloquea la descarga con un 451 y la denegación se recuerda durante 60 segundos para que los reintentos no vuelvan a preguntar.

Nota: A pesar de que la etiqueta de la interfaz menciona pip --require-hashes, la detección actualmente solo está implementada para la cabecera npm-command: ci de npm; las instalaciones de pip fijadas por hash no activan el aviso. (Tampoco se reescriben nunca, ya que los artefactos de PyPI nunca se modifican.)

Avisos de consentimiento y concesiones

Todas las rutas de cadena de suministro que preguntan al usuario —el paso directo por lockfile y la retención de paquete no verificado descrita más abajo— pasan por un agente de consentimiento compartido con agrupación de ráfagas: las peticiones concurrentes para el mismo ámbito esperan en un único cuadro de diálogo en lugar de apilar alertas. Cada aviso ofrece las mismas cuatro decisiones:

DecisiónEfecto
No permitirBloquea con un 451; se recuerda durante 60 segundos, denegando automáticamente los reintentos inmediatos.
Permitir una vezDeja pasar esta única petición (o ráfaga agrupada).
Permitir durante 15 minutosConcede el ámbito durante 15 minutos.
Permitir durante el resto de la sesiónConcede el ámbito hasta que la app se cierra.

Las concesiones y denegaciones activas se listan como Decisiones de cadena de suministro —separadas de las decisiones de salvaguardas— en la interfaz de aprobaciones (el área VentanaAprobaciones de credenciales…), donde puedes revocar una concesión antes de tiempo. Las concesiones son solo en memoria y no sobreviven a un reinicio de la app.

En las sesiones remotas SSH/CLI no hay cuadro de diálogo gráfico: la misma pregunta se representa como un selector dentro del tmux del espacio de trabajo. La ausencia de respuesta significa denegar. Consulta Acceso remoto.

Configurar la política

La política de cadena de suministro se configura por espacio de trabajo, en el panel Cadena de suministro de la ventana Editar espacio de trabajo (el icono amarillo de caja de envío en la barra lateral). La referencia completa campo por campo está en Ajustes de cadena de suministro; los valores predeterminados de un vistazo:

AjustePredeterminado
Rechazar paquetes más recientes que el umbral (control de antigüedad)Activado, Antigüedad mínima: 2 días, sin exenciones
Buscar paquetes en api.osv.dev (gratis, sin clave requerida)Desactivado; Bloquear a partir de la gravedad: Alta y superior
Filtrado de paquetesNinguno (sin clave de socket.dev ni de Delpi)
Bloquear paquetes comprometidos / Bloquear paquetes con CVE conocidos (socket.dev)Desactivado; Umbral de bloqueo de CVE: Alta y superior
Eliminar preinstall / install / postinstall / prepare de los tarballs de npm sobre la marchaDesactivado, lista de permitidos vacía
Preguntar antes de dejar pasar sin modificar los tarballs fijados por lockfileDesactivado (paso directo silencioso)

Tres detalles operativos:

  • Las ediciones se aplican en vivo. Guardar el espacio de trabajo envía la nueva política a las sesiones en ejecución de inmediato; el proxy la lee por petición, así que nunca hace falta reiniciar la VM. Cada política nueva o modificada se confirma con un resumen de una línea en el Registro de seguridad, por ejemplo:

    [supply-chain] policy engaged for 1a2b3c4d: age-gate=2d osv=high socket.dev=compromised+cve=high strip-scripts
    

    Las configuraciones incorrectas se señalan justo en ese resumen: socket.dev=key-set-but-no-toggle significa que se ha introducido una clave pero ninguno de los bloqueos está habilitado, y delpi=selected-but-no-key significa que se ha elegido Delpi pero está desactivado por falta de clave.

  • Dónde se almacena. La política reside en el profile.json del espacio de trabajo bajo ~/Library/Application Support/BromureAC/profiles/<id>/ (solo se escriben los campos que no son predeterminados). Las claves de API de socket.dev y de Delpi también se almacenan ahí —únicamente del lado del anfitrión, mostradas como campos seguros en la interfaz y nunca copiadas a la VM—. No hay variables de entorno ni argumentos de inicio específicos de la cadena de suministro.

  • Configuración remota. El menú remoto SSH/TUI expone el mismo panel Cadena de suministro con los mismos campos, de modo que una instancia sin interfaz puede configurarse sin la GUI. Consulta Acceso remoto.

La ventana del Registro de seguridad

Todo lo que hace la canalización de cadena de suministro es visible en el Registro de seguridad: abre VentanaRegistro de seguridad…. Es un seguimiento en vivo de cada evento de seguridad que emite el proxy: búsquedas y veredictos de cadena de suministro, bloqueos 451, eliminaciones de scripts, confirmaciones de política activada y reencaminamientos de Delpi, junto con detecciones de inyección de prompts, activación/desactivación de Fusion, cambios de enrutamiento de LLM, eventos de acceso remoto y líneas de worktree. Una sola ventana sirve para toda la app; elegir de nuevo la opción del menú la trae al frente.

Las filas tienen código de color para que puedas leer una instalación de un vistazo:

ColorSignificadoMarcador
AzulBúsqueda saliente (OSV, socket.dev, respaldo de fecha de publicación)
VerdeVeredicto limpio: el paquete pasó
RojoBloqueo o fallo (451, error)
NaranjaScripts de instalación eliminados de un tarball«eliminado»
AcentoPolítica activada / modificada[supply-chain]

La ventana tiene un campo Filtrar… para acotar la vista (escribe un nombre de paquete, un ecosistema o cualquier texto de las líneas que te interesen), una casilla Desplazamiento automático (activada de forma predeterminada; desmárcala para dejar de seguir el flujo mientras lees), un botón Borrar que vacía el búfer y un pie que muestra el recuento de entradas («42 entradas», o «7 de 42 entradas» mientras hay un filtro activo). Un mensaje de estado vacío explica cuándo aparecerán las entradas.

Para verlo en acción: abre la ventana, ejecuta cualquier instalación dentro de una sesión (npm install, pip install, cargo add, gem install, …) y observa cómo van apareciendo las entradas. Una instalación totalmente limpia sigue produciendo líneas de «inspección» para cada artefacto: eso es deliberado, para que una canalización silenciosa se distinga de una que ha sido evitada.

Nota: El búfer es un anillo en memoria limitado a unas 5.000 líneas y no persiste entre reinicios de la app. Cada línea también se refleja en el stderr de la app, de modo que lanzar bromure-cli desde una terminal (o capturar su salida de registro) te da una copia duradera. La ventana se abre a 820×460 y puede reducirse hasta 720×360.

Comportamiento sin conexión y degradado

Las comprobaciones de reputación de la canalización dependen de llamadas salientes desde tu Mac (nunca desde la VM): api.osv.dev para OSV, api.socket.dev para socket.dev, pypi.org para el respaldo de fecha de publicación de PyPI y depi-npm-proxy.landh.tech para Delpi. Cuando una fuente habilitada no puede producir un veredicto —red caída tras los reintentos, error HTTP, limitación de velocidad, un fallo de autenticación o un ecosistema no admitido— Bromure falla de forma cerrada en lugar de permitir silenciosamente.

La descarga se pausa y una retención de paquete no verificado te pregunta, por paquete y versión, con una alerta del sistema titulada ¿Dejar pasar un paquete no verificado del espacio de trabajo «…»?: Bromure no pudo alcanzar la fuente para examinar el paquete, e instalarlo significa aceptar un paquete que no se comprobó contra tus fuentes de reputación configuradas. Se aplican las cuatro decisiones de consentimiento estándar; denegar (incluso cuando no hay GUI disponible y el aviso remoto expira) bloquea con un 451. Las retenciones se indexan por paquete@versión, de modo que una única aprobación nunca puede cubrir todo el grafo de dependencias.

Esto es una protección deliberada: sin ella, un atacante que pueda inducir limitación de velocidad en el servicio de reputación podría colar paquetes sin comprobar. Las consecuencias prácticas:

  • Trabajar totalmente sin conexión con OSV o socket.dev habilitado significa un aviso por cada paquete no cacheado. Desactiva esas capas para los periodos sin conexión: el control de antigüedad sigue funcionando a partir de los metadatos ya cacheados sin conectividad.
  • socket.dev más Cargo significa un aviso por cada crate, ya que socket.dev no puede examinar Cargo en absoluto (las concesiones son por paquete@versión, de modo que una concesión de sesión solo cubre esa versión concreta del crate).
  • El respaldo de fecha de publicación de PyPI es la única excepción: ante un error de red puro falla de forma abierta para esa descarga (el fallo no se cachea, así que la búsqueda se reintenta la próxima vez). La reescritura de metadatos del control de antigüedad no se ve afectada.

Detección de compromisos

Las comprobaciones de cadena de suministro reducen las probabilidades de que código hostil entre en el sandbox; la detección de compromisos captura el momento en que código hostil que entró intenta actuar. Forma parte del sistema de credenciales y no de la canalización de paquetes —la imagen completa de las credenciales señuelo y el intercambio de tokens está en Credenciales—, pero su alarma se documenta aquí porque un paquete envenenado es su disparador más probable.

Cada petición saliente de la VM se escanea en una sola pasada en busca de los tokens señuelo («falsos») que acuñó la capa de intercambio de credenciales. Un señuelo limitado a un anfitrión (por ejemplo, github.com) que aparece en una petición destinada a cualquier otro anfitrión es la firma de una exfiltración de credenciales. Cuando eso ocurre:

  1. El proxy bloquea la petición con un 451: el destino nunca recibe un solo byte.
  2. La VM se pausa al instante. Si la sesión estaba desacoplada, se reacopla y se revela por la fuerza. El marco congelado de la sesión se tiñe de rojo.
  3. Aparece una alerta crítica, titulada Este entorno puede haber sido comprometido, que explica que Bromure detectó un intento saliente de filtrar una credencial de sesión desde el espacio de trabajo indicado hacia un anfitrión para el que no fue acuñada y que la VM se ha pausado, con una línea de detalle por cada fuga: una vista previa del token (como sk-a…f9q3), el nombre de la credencial, el anfitrión para el que se acuñó y el anfitrión hacia el que se observó que se dirigía.

La alerta ofrece tres respuestas:

  • Apagar (la opción predeterminada) — detiene la VM y marca el espacio de trabajo como comprometido.
  • Guardar para investigación — eliges una carpeta; Bromure exporta disk.img (una copia del disco de sistema de la VM), home.tar.gz (el directorio de inicio del espacio de trabajo) y shares/<name>.tar.gz por cada carpeta compartida, luego se apaga y marca el espacio de trabajo como comprometido. El estado de la RAM se descarta.
  • Continuar — aceptar el riesgo y reanudar; el detector vuelve a activarse si sucede de nuevo. Esc y Cmd-. deliberadamente no se asignan a Continuar: reanudar una VM posiblemente hostil debe ser un clic explícito.

Un espacio de trabajo marcado como comprometido se niega a arrancar de nuevo hasta que lo borres explícitamente, y el flujo de borrado advierte de que las carpetas compartidas no se borran: pueden seguir conteniendo archivos contaminados, así que revísalas a mano.

No hay nada que configurar: la detección siempre está activa para las credenciales que declaran un ámbito de anfitrión. Las credenciales manuales sin un anfitrión fijado («cualquier anfitrión») nunca pueden activarla, y los tokens de Claude/Codex usan una coincidencia relajada de mismo dominio registrado (un token acuñado para api.anthropic.com visto en otro anfitrión de anthropic.com no genera alarma). Solo se muestra una alerta a la vez; los eventos repetidos mientras está abierta se descartan.

Visibilidad empresarial

Para los espacios de trabajo inscritos en bromure.io, cada descarga de metadatos y de artefactos también emite un evento supply_chain.fetch al flujo de eventos empresariales, que lleva el ecosistema, el paquete, la versión, el tipo de petición, el resultado (allowed, rewritten, blocked o stripped) y el tipo de motivo (age_gate, osv, socket_compromised, socket_cve, verify_unavailable, lockfile_denied, scripts_stripped). La sola inscripción da a los administradores visibilidad de las descargas de paquetes de toda la organización —un inventario de todo lo que instaló cada agente— incluso en espacios de trabajo con todas las capas de aplicación desactivadas.

Cómo funciona la inscripción, y qué ven los administradores al otro extremo, se cubre en Empresa.