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 máquina en el anfitrión, antes de que un solo byte llegue a la VM.
Este capítulo explica qué se intercepta, cada comprobación del flujo, qué ocurre cuando una comprobación se activa y cómo leer los resultados en la Cronología de seguridad. La política de cadena de suministro se define por máquina (un espacio de trabajo, en el editor de ajustes). La referencia de ajustes campo por campo del panel está 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 Depi 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, Depi) 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
.tgzde npm, una wheel o sdist de Python, un.crate,.gem,.nupkgo el.zipde 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.
Cada obtención de paquete se clasifica y se registra en la Cronología de seguridad, sea cual sea la política; las comprobaciones en sí solo se ejecutan para las capas que actives. No hay nada que instalar ni configurar dentro de la VM.
Nota: Los paquetes que ya están en la caché local de npm o de pip nunca llegan a la red, así que no se comprueba —ni se registra— nada para ellos. Una Cronología silenciosa durante una instalación puede significar simplemente que todo vino 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:
| Resultado | Qué ocurre | Marcador visible |
|---|---|---|
| Permitido | La respuesta pasa intacta. | Fila verde comprobado / descargado en la Cronología de seguridad. |
| Reescrito | El 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; fila azul en la Cronología. |
| Bloqueado | La descarga se rechaza con una respuesta HTTP 451 («No disponible por razones legales»). | Cabecera X-Bromure-Block: supply-chain; fila roja en la Cronología. |
| Retenido para consentimiento | La descarga se pausa mientras Bromure te pregunta: para los pasos directos fijados por lockfile, para paquetes que ninguna fuente de reputación habilitada pudo examinar y para paquetes que OSV o socket.dev marcaron (consulta Confiar en un paquete bloqueado). 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:
| Ecosistema | Anfitriones interceptados | Control de antigüedad | OSV | socket.dev | Depi | Eliminación de scripts |
|---|---|---|---|---|---|---|
| npm | registry.npmjs.org, *.npmjs.org | Sí | Sí | Sí | Sí | Sí |
| PyPI | pypi.org, files.pythonhosted.org | Sí | Sí | Sí | — | — |
| Cargo | crates.io, static.crates.io, index.crates.io | Sí | Sí | No | — | — |
| RubyGems | rubygems.org | Sí | Sí | Sí | — | — |
| Maven Central | repo1.maven.org, repo.maven.apache.org, search.maven.org | No | Sí | Sí | — | — |
| NuGet | api.nuget.org, *.nuget.org | No | Sí | Sí | — | — |
| Módulos de Go | proxy.golang.org | No | Sí | Sí | — | — |
| Packagist | repo.packagist.org, packagist.org | Sí | Sí | Sí | — | — |
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.iono produce veredicto y, por lo tanto, activa el aviso de consentimiento de paquete no verificado (consulta Comportamiento sin conexión y degradado). Si tu máquina 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.
Funciona mediante dos mecanismos que colaboran:
- Reescritura de metadatos. El proxy elimina las versiones demasiado recientes del listado de versiones del registro y reorienta
latesty 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>/jsonpara obtener la fecha de publicación.
Para configurarlo, abre el panel Cadena de suministro de la máquina y usa el grupo Control de antigüedad:
- Activa Rechazar paquetes más recientes que el umbral.
- Fija Antigüedad mínima: con el selector incremental (0–90 días).
- 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:axioslimita la exención a un ecosistema, mientras que unaxiosa 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:
- Activa Buscar paquetes en api.osv.dev (gratis, sin clave requerida).
- 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 Depi
El grupo Filtrado de paquetes selecciona un proveedor de reputación mediante un selector mutuamente excluyente: Ninguno, socket.dev o Depi. Los dos proveedores funcionan de maneras fundamentalmente distintas: socket.dev es una consulta previa que el proxy realiza antes de dejar pasar una descarga; Depi 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
postinstallbenigno 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 por defecto. 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 espera progresiva. Los errores de autenticación se escriben en el registro de la app 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.
Depi
Depi 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 Depi en depi-npm-proxy.landh.tech:443, adjuntando tu clave como cabecera Authorization: Bearer y eliminando cualquier cabecera Authorization que envíe el invitado. Depi 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 Depi en el botón de opción 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 dice Introduce una clave de API: Depi permanece desactivado sin ella. Cada solicitud redirigida se escribe en el registro de la app:
[depi] 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 Depi registry rejected the configured API key…», con la cabecera
X-Bromure-Block: depi-auth), lo registra y genera una alerta de interfaz gráfica única por cada combinación de espacio de trabajo y clave: «Depi 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 Depi, o clave no autorizada para él). Se registra y se pasa sin cambios, de modo que npm reporta el texto de rechazo propio de Depi.
Depi afecta únicamente a npm; los otros siete ecosistemas no se ven afectados por él. Depi 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 Depi 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.
Nota: Esta capa del proxy es independiente de la política de scripts de instalación propia de npm dentro de la VM. Las versiones recientes de npm solo ejecutan scripts de instalación para los paquetes de una lista
allow-scripts, así que el~/.npmrcgestionado por Bromure en cada máquina los permite exactamente para las CLI de agentes que incluye —@anthropic-ai/claude-code,@openai/codex,@moonshot-ai/kimi-codey@oh-my-pi/pi-coding-agent— para que un agente que se actualiza connpm install -gobtenga su binario nativo. Cualquier otro paquete sigue el comportamiento predeterminado de npm. Si activas aquí la eliminación de scripts, añade también esos paquetes a Permitir scripts de instalación para, o las autoactualizaciones de los agentes pierden su paso de instalación.
El interruptor está desactivado de forma predeterminada, y se aplican dos límites:
- Solo npm. Los sdists de PyPI no se reescriben:
setup.pyes 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.jsonno puede reescribirse sin fallar la verificación. Ese caso lo gobierna la siguiente capa.
Instalaciones fijadas por lockfile
Una instalación fijada por lockfile —npm 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 cabeceranpm-command: cide 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.)
Confiar en un paquete bloqueado
Un veredicto de reputación no siempre es acertado para ti: socket.dev marca workerd por su script de instalación, y sin embargo es el runtime de Cloudflare, que tu proyecto puede necesitar. En lugar de un 451 silencioso, un bloqueo de OSV o socket.dev en la descarga de un paquete la retiene y pregunta: ¿Instalar … desde el espacio de trabajo «…» a pesar de su aviso de cadena de suministro?, citando el motivo. Las opciones son:
| Opción | Efecto |
|---|---|
| Permitir … durante esta sesión | Lo instala, y cualquier otra versión de ese paquete, hasta que la app se cierre. |
| Permitir siempre … en este espacio de trabajo | Lo instala y lo añade a la lista Paquetes de confianza del espacio de trabajo, de modo que las descargas posteriores se saltan las comprobaciones de OSV y socket.dev para él, mientras siga en la lista. |
| No permitir … y no volver a preguntar en esta sesión | Mantiene el 451; la misma instalación sigue bloqueada sin volver a preguntar hasta que la app se cierre. |
| Ahora no | Mantiene el 451 por ahora; se vuelve a preguntar al cabo de un minuto. No responder equivale a esto. |
Un veredicto de malware de socket.dev (malware, malware conocido, malware detectado por GPT, paquete troll, clave SSH comprometida) nunca se ofrece para anularlo y nunca queda cubierto por la lista: esas descargas siempre se bloquean. El control de antigüedad conserva su propia lista Paquetes exentos, y la eliminación de scripts de instalación su lista Permitir scripts de instalación para; confiar aquí en un paquete no lo exime de ninguna de las dos.
También puedes añadir un paquete a posteriori: una fila bloqueada en la Cronología de seguridad ofrece Permitir… → Permitir siempre … en este espacio de trabajo, y la lista en sí está en el panel Cadena de suministro, en Paquetes de confianza (formato npm:workerd). Los cambios se aplican al instante a una máquina en ejecución.
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. El paso directo por lockfile ofrece estas cuatro decisiones (la retención de paquete no verificado tiene las suyas, por paquete; consulta Comportamiento sin conexión y degradado):
| Decisión | Efecto |
|---|---|
| No permitir | Bloquea con un 451; se recuerda durante 60 segundos, denegando automáticamente los reintentos inmediatos. |
| Permitir una vez | Deja pasar esta única petición (o ráfaga agrupada). |
| Permitir durante 15 minutos | Concede el ámbito durante 15 minutos. |
| Permitir durante el resto de la sesión | Concede 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 Ventana → Aprobaciones 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 SSH/CLI remotas no hay diálogo gráfico: la misma pregunta se presenta como un selector dentro del tmux de la máquina. No responder significa denegar. Consulta Acceso remoto.
Configurar la política
La política de cadena de suministro se configura por máquina, en el panel Cadena de suministro, en Seguridad dentro de los ajustes de la máquina. La referencia completa campo por campo está en Ajustes de Cadena de suministro; los valores predeterminados de un vistazo:
| Ajuste | Predeterminado |
|---|---|
| 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 paquetes | Ninguno (sin clave de socket.dev ni de Depi) |
| 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 marcha | Desactivado, lista de permitidos vacía |
| Preguntar antes de dejar pasar sin modificar los tarballs fijados por lockfile | Desactivado (paso directo silencioso) |
Tres detalles operativos:
-
Las ediciones se aplican en vivo. Guardar los ajustes envía la nueva política a las sesiones en ejecución de inmediato: el proxy la lee en cada solicitud, 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 la app, por ejemplo:
[supply-chain] policy engaged for 1a2b3c4d: age-gate=2d osv=high socket.dev=compromised+cve=high strip-scriptsLas configuraciones incorrectas se señalan justo en ese resumen:
socket.dev=key-set-but-no-togglesignifica que se ha introducido una clave pero ninguno de los bloqueos está habilitado, ydepi=selected-but-no-keysignifica que se ha elegido Depi pero está desactivado por falta de clave. -
Dónde se almacena. La política reside en el
profile.jsondel 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 Depi 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.
Seguir las instalaciones en la Cronología de seguridad
Todo lo que decide el flujo de cadena de suministro es visible en la Cronología de seguridad: abre Ventana → Seguridad… o búscala con ⌘K. El mosaico Paquetes comprobados de su pestaña Resumen cuenta las obtenciones de paquetes de las últimas 24 horas; haz clic en él, o elige Cadena de suministro en el filtro de motores de la pestaña Cronología, para verlas una por fila.
Cada fila nombra el paquete en su columna Condición (npm · [email protected]) e indica qué pasó en su columna Decisión, con código de colores:
| Decisión | Color | Significado |
|---|---|---|
| comprobado | Verde | Se obtuvo la lista de versiones de un paquete y pasó. |
| descargado | Verde | Se obtuvo un artefacto de paquete y pasó. |
| comprobado: versiones más nuevas que el periodo de espera ocultas | Azul | El control de antigüedad reescribió una lista de versiones. Es el control haciendo su trabajo normal, no un bloqueo. |
| scripts de instalación despojados | Azul | Se eliminaron los hooks de instalación de un tarball de npm. |
| bloqueado: … | Rojo | La obtención se rechazó con un 451; el motivo sigue a los dos puntos (por ejemplo bloqueado: malware conocido). Un bloqueo que puedes anular tiene un botón Permitir… al final de la fila. |
| descargado — permitido por ti a pesar de: … | Azul | Un paquete que OSV o socket.dev marcaron, instalado porque lo permitiste (el aviso, o Paquetes de confianza). |
Las obtenciones de paquetes se registran siempre, esté o no activa alguna capa de cadena de suministro: la aplicación sigue la política, el registro no. Las instalaciones de apt también se registran: cada .deb obtenido del pool de un archivo de Ubuntu o Debian aparece como una fila apt. Solo se observan; ninguna de las comprobaciones de este capítulo se les aplica.
Para verlo en acción: abre la Cronología, ejecuta cualquier instalación dentro de una sesión (npm install, pip install, cargo add, gem install, apt install, …) y mira cómo llegan las filas. Los eventos se conservan en disco durante 90 días y sobreviven a los reinicios de la app; Borrar solo oculta las filas actuales de la ventana. Exportar… guarda lo que muestra la pestaña como archivo CSV.
Nota: La Cronología registra decisiones, no cada paso. Las líneas detalladas del flujo —cada consulta saliente, los resúmenes «policy engaged», las redirecciones de Depi, los errores de API— van al registro de la app en
~/Library/Logs/BromureAC/bromure-ac.log(y al stderr de la app cuando se lanza desde un terminal).
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 Depi. 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, en un panel titulado ¿Instalar paquete versión desde el espacio de trabajo «…» sin comprobar su reputación?. Explica por qué no hay veredicto —socket.dev no respondió, rechazó la clave de API (revísala en los ajustes de Cadena de suministro del espacio de trabajo), está limitando la clave, aún no ha terminado de analizar esa versión o no conoce la versión (puede ser muy reciente)— y que instalarlo significa aceptar un paquete que no se comprobó contra tus fuentes de reputación configuradas. Las opciones:
- Permitir paquete versión — esa versión se instala y no vuelve a preguntar durante el resto de la sesión.
- Permitir todos los paquetes sin verificar durante 15 minutos — este y cualquier otro paquete del espacio de trabajo que no obtenga veredicto en los 15 minutos siguientes.
- No permitir paquetes sin verificar durante el resto de la sesión — este y todos los posteriores se rechazan sin preguntar.
- No permitir paquete versión — rechazado (con un 451). No responder antes de que acabe la cuenta atrás, pulsar Esc o cerrar el panel equivale a lo mismo.
Solo hay una pregunta en pantalla por espacio de trabajo a la vez: cuando un uv sync o un npm install se topa con varios paquetes sin verificar, se preguntan uno tras otro (el panel dice cuántos esperan), y una respuesta para todos resuelve el resto de golpe.
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 sí 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:
- El proxy bloquea la petición con un 451: el destino nunca recibe un solo byte.
- La VM se pausa al instante. Si la máquina se estaba ejecutando en segundo plano, primero se trae a la pantalla. El fotograma congelado se tiñe de rojo.
- 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 la máquina nombrada hacia un host para el que no fue emitida y que la VM se ha pausado, con una línea de detalle por filtración: una vista previa del token (como
sk-a…f9q3), el nombre de la credencial, el host para el que fue emitida y el host hacia el que se observó que se dirigía.
La alerta ofrece cuatro respuestas:
- Apagar (la opción predeterminada): detiene la VM y marca la máquina como comprometida.
- Bloquear: detiene solo esta solicitud y reanuda la VM; el detector vuelve a dispararse si el agente lo intenta de nuevo.
- Permitir: confía en el host observado durante el resto de esta sesión; la credencial se sustituye y se envía allí, y las solicitudes hacia él dejan de marcarse. Úsalo solo para un destino que reconozcas.
- Guardar para investigación: eliges una carpeta; Bromure exporta
disk.img(una copia del disco del sistema de la VM),home.tar.gz(el directorio personal de la máquina) yshares/<name>.tar.gzpor cada carpeta compartida, luego apaga y marca la máquina como comprometida. El estado de la RAM se descarta.
Solo Apagar responde a Retorno; las otras tres requieren un clic explícito. Un cliente remoto que refleja la máquina recibe Apagar, Bloquear y Permitir. La descripción completa, incluida la lista de permitidos por sesión detrás de Permitir, está en Credenciales.
Una máquina marcada como comprometida se niega a arrancar de nuevo hasta que la borres explícitamente, y el flujo de borrado advierte que las carpetas compartidas no se borran: pueden contener todavía archivos contaminados, así que revísalas a mano.
La detección está siempre activa para las credenciales que declaran un alcance de host. Las credenciales manuales sin host fijado («cualquier host») nunca pueden activarla, y los tokens de Claude/Codex usan una coincidencia relajada por mismo dominio registrado (un token emitido para api.anthropic.com visto en otro host de anthropic.com no da la alarma). Solo se muestra una alerta a la vez; los eventos repetidos mientras está abierta se descartan. Una máquina puede suprimir la alerta y la pausa con No alertar sobre la exfiltración de credenciales en su panel Salvaguardas; la filtración sigue bloqueándose y sigue apareciendo como una fila roja Intermediación de credenciales en la Cronología de seguridad (consulta Vías de escape).
Visibilidad empresarial
Para las máquinas de un Mac inscrito 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 máquinas 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.