Protection de la chaîne d'approvisionnement
Un agent de codage autonome installe des paquets en permanence : la génération d'un projet tire des centaines de dépendances sans qu'un humain n'en lise jamais le nom, encore moins un journal des modifications. Cela fait du registre de paquets le canal le plus large par lequel du code hostile peut entrer dans le bac à sable : un nom typosquatté, une version fraîchement compromise ou un script postinstall malveillant s'exécute avec toutes les permissions de l'agent dès que npm install se termine. Bromure Agentic Coding traite chaque récupération de paquet comme une entrée non fiable et lui applique une politique par espace de travail sur l'hôte, avant qu'un seul octet n'atteigne la VM.
Ce chapitre explique ce qui est intercepté, chaque vérification du pipeline, ce qui se passe lorsqu'une vérification se déclenche, et comment lire les résultats dans le Journal de sécurité. La référence champ par champ des paramètres du volet se trouve dans Paramètres Chaîne d'approvisionnement.
Pourquoi les installations de paquets constituent la surface d'attaque
Le bac à sable contient déjà l'agent : il ne peut pas toucher votre Mac, et ses identifiants sont des leurres (voir Identifiants). Ce que le bac à sable ne peut pas faire à lui seul, c'est juger la provenance du code que l'agent tire. Les attaques de la chaîne d'approvisionnement exploitent exactement cette faille :
- Compromissions de versions récentes. Le compte d'un mainteneur est compromis et une version malveillante est publiée. Ces versions sont généralement détectées et retirées en quelques heures ou quelques jours — c'est pourquoi la barrière d'ancienneté refuse les versions plus récentes qu'un seuil.
- Versions vulnérables connues. L'agent résout une dépendance vers une version ayant un avis de sécurité publié. La vérification OSV détecte ces cas.
- Logiciels malveillants, typosquats et scripts d'installation malhonnêtes. Des paquets conçus pour être malveillants dès le départ. Les fournisseurs socket.dev et Delpi les signalent ou les filtrent, et le retrait des scripts d'installation supprime purement et simplement le vecteur d'exécution le plus courant.
L'application est conçue pour se faire côté hôte. Le proxy applique la politique à la réponse avant que l'agent ne la voie, de sorte que rien de ce qui s'exécute dans la VM — y compris un agent entièrement compromis — ne peut assouplir les règles. Le .npmrc et le pip.conf internes à la VM ne peuvent que restreindre davantage ce que le proxy a déjà servi, jamais l'élargir. Les clés d'API des services de réputation (socket.dev, Delpi) sont conservées uniquement sur l'hôte et ne sont jamais exportées dans la VM.
Comment fonctionne l'interception
Chaque requête réseau émise par la VM passe par le proxy MITM côté hôte (voir Concepts). Le proxy reconnaît les requêtes vers les principaux registres de paquets et classe chacune d'elles :
- Métadonnées — la liste des versions d'un paquet (un packument npm, l'API JSON ou l'index
/simple/de PyPI, une entrée d'index sparse Cargo, et ainsi de suite). La barrière d'ancienneté opère ici, en réécrivant la liste. - Artefact — le fichier téléchargeable pour une version précise : un
.tgznpm, une wheel ou une sdist Python, un.crate,.gem,.nupkg, ou un.zipde module Go. Les recherches OSV, les vérifications socket.dev, le retrait des scripts et les blocages fermes se produisent tous au moment de la récupération de l'artefact. - Passe-plat — tout le reste sur ces hôtes (recherche, authentification). Intact.
L'interception est automatique dès qu'au moins une couche de chaîne d'approvisionnement est activée pour l'espace de travail (ou que l'espace de travail est inscrit auprès de bromure.io, auquel cas les récupérations sont observées à des fins de télémétrie même avec toutes les couches d'application désactivées). Il n'y a rien à installer ni à configurer à l'intérieur de la VM.
Remarque : Les paquets déjà présents dans le cache local de npm ou de pip ne passent jamais par le réseau, donc rien n'est vérifié — ni consigné — pour eux. Un Journal de sécurité silencieux pendant une installation peut simplement signifier que tout provenait du cache.
Verdicts : autoriser, réécrire, bloquer, mettre en attente
Les couches de chaîne d'approvisionnement sont des bascules individuelles marche/arrêt, chacune avec une action fixe — il n'y a pas de mode global bloquer/avertir/autoriser pour ce volet. (Le bouton radio à trois positions consigner/demander/bloquer que vous connaissez peut-être de Injection de prompt, et les modes d'application dans Garde-fous, sont des systèmes distincts.) Chaque récupération se termine par l'un des quatre résultats :
| Résultat | Ce qui se passe | Marqueur visible |
|---|---|---|
| Autorisé | La réponse passe intacte. | Coche verte dans le Journal de sécurité ; une ligne « inspection » confirme que le proxy l'a vue. |
| Réécrit | Le proxy a modifié la réponse : versions trop récentes retirées des métadonnées, ou scripts d'installation retirés d'une archive. Le gestionnaire de paquets se poursuit normalement. | En-tête de réponse X-Bromure-Rewritten: supply-chain ; lignes oranges « retiré » dans le journal. |
| Bloqué | Le téléchargement est refusé avec une réponse HTTP 451 (« Unavailable For Legal Reasons »). | En-tête X-Bromure-Block: supply-chain ; ligne rouge dans le journal. |
| Mis en attente de consentement | Le téléchargement est suspendu pendant que Bromure vous interroge — pour les passe-plats épinglés par lockfile et pour les paquets qu'aucune source de réputation activée n'a pu vérifier. Refuser transforme la mise en attente en blocage 451. | Une alerte système de consentement ; la décision est consignée. |
Un blocage 451 comporte un corps en texte brut qui commence par Bromure Supply-Chain Security blocked this request: suivi de la raison exacte, par exemple :
Bromure Supply-Chain Security blocked this request: npm package [email protected]
published 4 hours ago — policy requires 2 days minimum
npm, pip, cargo et les autres gestionnaires de paquets impriment ce corps textuellement dans leur sortie d'erreur, de sorte que l'agent comme vous voyez précisément pourquoi une installation a échoué — et l'agent peut souvent contourner le problème par lui-même (par exemple en épinglant une version plus ancienne). Le statut 451 a été choisi délibérément pour que les blocages de chaîne d'approvisionnement se distinguent d'un coup d'œil des 403 qu'utilise Garde-fous.
Couverture par écosystème
Le proxy intercepte huit écosystèmes de paquets. Toutes les vérifications ne prennent pas en charge tous les écosystèmes :
| Écosystème | Hôtes interceptés | Barrière d'ancienneté | OSV | socket.dev | Delpi | Retrait de scripts |
|---|---|---|---|---|---|---|
| npm | registry.npmjs.org, *.npmjs.org | Oui | Oui | Oui | Oui | Oui |
| PyPI | pypi.org, files.pythonhosted.org | Oui | Oui | Oui | — | — |
| Cargo | crates.io, static.crates.io, index.crates.io | Oui | Oui | Non | — | — |
| RubyGems | rubygems.org | Oui | Oui | Oui | — | — |
| Maven Central | repo1.maven.org, repo.maven.apache.org, search.maven.org | Non | Oui | Oui | — | — |
| NuGet | api.nuget.org, *.nuget.org | Non | Oui | Oui | — | — |
| Modules Go | proxy.golang.org | Non | Oui | Oui | — | — |
| Packagist | repo.packagist.org, packagist.org | Oui | Oui | Oui | — | — |
Deux lacunes à connaître :
- Barrière d'ancienneté — Maven, NuGet, Go. Ces trois écosystèmes ne transportent pas d'horodatages de publication par version dans leurs réponses de métadonnées standard, donc leurs métadonnées passent sans filtrage et le filet de sécurité au niveau de l'artefact n'a aucune donnée sur laquelle agir. La barrière d'ancienneté ne les bloque effectivement pas aujourd'hui ; npm, PyPI, Cargo, RubyGems et Packagist sont entièrement couverts.
- socket.dev — Cargo. socket.dev ne prend pas en charge Cargo. Avec le filtrage socket.dev activé, chaque artefact
crates.ione produit aucun verdict et déclenche donc l'invite de consentement pour paquet non vérifié (voir Comportement hors ligne et dégradé). Si votre espace de travail fait beaucoup de travail en Rust, répondez aux invites ou choisissez un autre fournisseur.
La barrière d'ancienneté
La barrière d'ancienneté refuse les versions de paquets plus récentes qu'un nombre de jours configurable, partant du principe qu'une version fraîchement publiée est la plus susceptible d'être un paquet tout juste compromis — les versions malveillantes sont généralement signalées et retirées rapidement, et patienter le temps de cette fenêtre ne coûte presque rien pour le développement courant. C'est la seule couche activée par défaut, avec un minimum de 2 jours.
Elle fonctionne au moyen de deux mécanismes coopérants :
- Réécriture des métadonnées. Le proxy retire les versions trop récentes de la liste des versions du registre et repointe
latestet les autres dist-tags vers la version survivante la plus récente. Les références flottantes —pkg@latest, les plages semver — ne sont donc jamais bloquées fermement : le gestionnaire de paquets se résout silencieusement vers la version la plus récente suffisamment ancienne pour passer. Du point de vue de l'agent, les versions plus récentes que le seuil n'existent tout simplement pas encore. - Filet de sécurité à la récupération de l'artefact. Chaque horodatage de publication par version que le proxy voit dans les métadonnées est mis en cache en mémoire (jusqu'à 50 000 entrées). Si l'agent demande ensuite directement une version épinglée trop récente, la récupération est bloquée par un 451 qui indique l'âge réel du paquet et le minimum requis. Pour pip — dont l'index HTML PEP 503 par défaut ne transporte aucun horodatage — Bromure effectue une recherche à la demande sur
https://pypi.org/pypi/<pkg>/<version>/jsonpour obtenir l'heure de publication.
Pour la configurer, ouvrez le volet Chaîne d'approvisionnement de l'espace de travail et utilisez le groupe Barrière d'ancienneté :
- Activez Refuser les paquets plus récents que le seuil.
- Réglez Âge minimum : avec le pas à pas (0 à 90 jours).
- Si un paquet spécifique doit être installable immédiatement — par exemple, votre propre équipe le publie — ajoutez-le sous Paquets exemptés avec le bouton Ajouter une entrée. Les entrées utilisent le format de liste d'autorisation :
npm:axioslimite l'exemption à un écosystème, un simpleaxioscorrespond au nom du paquet dans tous les écosystèmes. La correspondance est insensible à la casse.
Astuce : Les requêtes de métadonnées npm sont silencieusement mises à niveau vers le packument complet afin que la barrière puisse voir les heures de publication — vous n'avez rien à faire pour le format de métadonnées abrégé de npm.
Vérification des vulnérabilités OSV
La vérification OSV recherche l'écosystème, le paquet et la version de chaque artefact téléchargé sur api.osv.dev — la base de données gratuite Open Source Vulnerabilities, qui agrège la GitHub Advisory Database, les avis PyPI, la base de données de vulnérabilités de Go, RubySec et d'autres. Si un avis pour cette version exacte atteint ou dépasse la gravité que vous avez choisie, le téléchargement est bloqué par un 451. Elle couvre les huit écosystèmes et ne nécessite aucune clé d'API.
La gravité est reprise de l'étiquette GHSA lorsque l'avis en comporte une ; sinon, Bromure calcule le score de base CVSS v3 à partir de la chaîne vectorielle de l'avis.
Dans le groupe Vérification des vulnérabilités OSV :
- Activez Rechercher les paquets sur api.osv.dev (gratuit, aucune clé requise).
- Choisissez Bloquer à la gravité : — Faible et au-dessus, Moyenne et au-dessus, Élevée et au-dessus, ou Critique uniquement.
La vérification est désactivée par défaut, et le seuil de gravité est réglé par défaut sur Élevée et au-dessus — comme le volet lui-même le note, une CVE de faible gravité dans un sous-paquet transitif ne devrait pas interrompre un flux de travail. Les recherches ne s'exécutent que sur les téléchargements d'artefacts (pas sur les récupérations de métadonnées), les résultats sont mis en cache en mémoire pour la durée d'exécution de l'application, au plus 16 recherches s'exécutent en parallèle, et les erreurs réseau transitoires sont réessayées jusqu'à 5 fois avec temporisation. Si OSV ne peut pas du tout être joint, le paquet est mis en attente de votre consentement plutôt que silencieusement autorisé — voir Comportement hors ligne et dégradé.
Filtrage des paquets : socket.dev et Delpi
Le groupe Filtrage des paquets sélectionne un fournisseur de réputation via un bouton radio mutuellement exclusif : Aucun, socket.dev, ou Delpi. Les deux fournisseurs fonctionnent de manières fondamentalement différentes — socket.dev est une recherche préalable que le proxy consulte avant de laisser passer une récupération ; Delpi remplace purement et simplement le registre npm. Un seul peut être actif à la fois par conception (exécuter les deux doublerait le filtrage de chaque installation), et sélectionner Aucun désactive les deux tout en conservant les clés stockées. Quel que soit le fournisseur que vous choisissez, les autres couches de ce chapitre — barrière d'ancienneté, OSV, retrait de scripts — s'appliquent quand même par-dessus.
La valeur par défaut est Aucun. Les espaces de travail créés avant l'existence du bouton radio qui possèdent déjà une clé socket.dev sont automatiquement déduits comme socket.dev.
socket.dev
socket.dev est un service commercial de réputation de paquets. Avec socket.dev sélectionné et une clé d'API saisie, le proxy vérifie chaque téléchargement d'artefact par rapport à l'API d'incidents de socket.dev avant de le servir. Il prend en charge npm, PyPI, Go, Maven, RubyGems, NuGet et Packagist — mais pas Cargo (voir Couverture par écosystème).
Vous apportez votre propre clé : cliquez sur Obtenir une clé d'API à côté du champ Clé d'API : (il ouvre socket.dev/dashboard/settings/api-tokens), créez un jeton et collez-le dans le champ sécurisé. Les deux bascules de blocage restent désactivées tant qu'une clé n'est pas saisie, et une clé vide désactive entièrement socket.dev quelles que soient les bascules. La clé est stockée côté hôte dans le profile.json de l'espace de travail et n'entre jamais dans la VM ; toutes les recherches proviennent de votre Mac.
Deux blocages indépendants sont disponibles :
- Bloquer les paquets compromis (scripts d'installation malhonnêtes, signalés comme malveillants, typosquats, télémétrie suspecte) — se déclenche sur les incidents de risque de chaîne d'approvisionnement à forme d'attaque de socket.dev. Les signaux définitifs de logiciel malveillant (malware, malware connu, malware détecté par GPT, paquets troll, clés SSH compromises) bloquent à toute gravité. Les signaux à forme d'attaque plus bruyants — code obfusqué, chaînes suspectes, scripts d'installation, typosquatting, accès shell, usage HTTPS inhabituel — ne bloquent que lorsque socket.dev les évalue comme élevés ou critiques, de sorte qu'un paquet avec un
postinstallbénin n'est pas attrapé. Les signaux purement de qualité et d'attribut (nouvel auteur, lectures de variables d'environnement, accès réseau, télémétrie) ne bloquent délibérément jamais. - Bloquer les paquets avec des CVE connues — se déclenche sur la catégorie de vulnérabilités de socket.dev à partir du sélecteur Seuil de blocage CVE : (les quatre mêmes niveaux qu'OSV ; par défaut Élevée et au-dessus).
Les deux blocages sont désactivés par défaut. Les résultats sont mis en cache pour la durée d'exécution de l'application, au plus 16 appels s'exécutent en parallèle, et les échecs transitoires sont réessayés 5 fois avec temporisation. Les erreurs d'authentification sont remontées dans le Journal de sécurité avec le statut HTTP et un aperçu du corps de la réponse.
Remarque : Si vous activez OSV et le blocage CVE de socket.dev ensemble, les deux vérifieront chaque artefact — c'est autorisé (ce sont des couches différentes), simplement redondant pour la couverture CVE. De nombreux utilisateurs associent plutôt le blocage des paquets compromis de socket.dev à la vérification OSV gratuite.
Delpi
Delpi est un registre npm sécurisé prêt à l'emploi de Lupin & Holmes (landh.tech) qui sert des paquets pré-vérifiés. Au lieu de rechercher les paquets, Bromure redirige chaque requête de registre npm — métadonnées, archives, audit, tout ce qui est adressé à registry.npmjs.org ou *.npmjs.org — vers le registre de filtrage compatible npm de Delpi à depi-npm-proxy.landh.tech:443, en attachant votre clé sous forme d'en-tête Authorization: Bearer et en supprimant tout en-tête Authorization envoyé par l'invité. Delpi réécrit les URL d'archives dans ses packuments pour pointer vers lui-même, de sorte que ces récupérations ultérieures reçoivent aussi la clé injectée côté hôte.
Pour l'activer, sélectionnez Delpi dans le bouton radio et collez votre clé dans le champ sécurisé Clé d'API : (Obtenir une clé d'API ouvre landh.tech). Tant que le champ est vide, un avertissement orange affiche Saisissez une clé d'API — Delpi reste désactivé sans clé. Chaque requête redirigée est consignée dans le Journal de sécurité :
[delpi] GET registry.npmjs.org/… → https://depi-npm-proxy.landh.tech
La gestion des erreurs est explicite plutôt que silencieuse :
- 401 (clé rejetée). Bromure substitue une erreur en texte brut claire que npm imprime (« Bromure: the Delpi registry rejected the configured API key… », avec l'en-tête
X-Bromure-Block: delpi-auth), la consigne, et déclenche une alerte graphique unique par combinaison espace-de-travail-et-clé : « Delpi rejected your API key ». Les sessions SSH/TUI sans interface graphique ne reçoivent pas l'alerte graphique ; elles s'appuient sur la ligne de journal et l'erreur npm réécrite. - 403 (paquet bloqué par Delpi, ou clé non autorisée pour celui-ci). Consigné et transmis tel quel, de sorte que npm rapporte le texte de refus propre à Delpi.
Delpi n'affecte que npm — les sept autres écosystèmes ne sont pas touchés par lui. Delpi remplace socket.dev dans le bouton radio, pas votre politique locale : la barrière d'ancienneté, la vérification OSV et le retrait de scripts s'exécutent toujours sur ce que Delpi sert.
Retrait des scripts d'installation
Les hooks preinstall, install, postinstall et prepare de npm exécutent du code arbitraire au moment de l'installation et sont le cheval de bataille des logiciels malveillants npm du monde réel. Avec Retirer preinstall / install / postinstall / prepare des archives npm à la volée activé (dans le groupe Scripts d'installation), le proxy réécrit chaque archive npm en transit : il décompresse le .tgz, localise le package/package.json de premier niveau, supprime ces quatre clés de scripts, recalcule la somme de contrôle de l'en-tête tar, et recompresse.
Comme les octets de l'archive changent, le proxy nettoie aussi dist.integrity et dist.shasum des métadonnées de registre du paquet, de sorte que npm calcule lui-même le hachage à partir de l'archive nettoyée et que sa vérification passe toujours pour les installations non épinglées. Chaque retrait est consigné (« scripts d'installation retirés de » le paquet et la version, affiché en orange) ; les archives qui ont été inspectées et jugées propres sont marquées uniquement de l'en-tête X-Bromure-Rewritten: supply-chain. En cas d'échec d'analyse, l'archive originale passe intacte — cette couche échoue en mode ouvert, car elle ne doit jamais bloquer une installation bien formée.
Certains paquets ont réellement besoin de scripts d'installation — les compilateurs de liaisons natives comme better-sqlite3 ou node-canvas. Ajoutez-les à Autoriser les scripts d'installation pour (format npm:better-sqlite3) et ils conservent leurs hooks.
La bascule est désactivée par défaut, et deux limites s'appliquent :
- npm uniquement. Les sdists PyPI ne sont pas réécrites —
setup.pyest du code arbitraire, donc le retrait n'y est pas réalisable. - Installations non épinglées uniquement. Une archive dont le hachage d'intégrité est épinglé dans
package-lock.jsonne peut pas être réécrite sans faire échouer la vérification. Ce cas est régi par la couche suivante.
Installations épinglées par lockfile
Une installation épinglée par lockfile — npm ci — épingle le hachage d'intégrité de chaque archive dans le lockfile. Bromure ne peut pas réécrire ces archives sans briser la vérification du hachage, ses seules options sont donc de les transmettre sans modification ou de les bloquer. Par défaut, elles passent silencieusement.
Si vous voulez avoir votre mot à dire, activez Demander avant de transmettre les archives épinglées par lockfile sans modification (npm ci, pip --require-hashes) dans le groupe Installations épinglées par lockfile. La première récupération épinglée par lockfile d'un lot (détectée pour npm via l'en-tête de requête npm-command: ci) fait alors apparaître une boîte de dialogue de consentement hôte intitulée Transmettre npm ci (installation épinglée par lockfile) depuis l'espace de travail « … » ? avec les boutons Autoriser pendant 15 minutes, Autoriser une fois, Autoriser pour le reste de la session, et Ne pas autoriser. Toute la rafale de récupérations concurrentes se regroupe sur cette seule invite et suit votre décision ; refuser bloque la récupération par un 451 et le refus est mémorisé pendant 60 secondes afin que les nouvelles tentatives ne redemandent pas.
Remarque : Bien que l'étiquette de l'interface mentionne
pip --require-hashes, la détection n'est actuellement implémentée que pour l'en-têtenpm-command: cide npm — les installations pip épinglées par hachage ne déclenchent pas l'invite. (Elles ne sont non plus jamais réécrites, puisque les artefacts PyPI ne sont jamais modifiés.)
Invites de consentement et autorisations
Tous les chemins de chaîne d'approvisionnement qui interrogent l'utilisateur — le passe-plat par lockfile et la mise en attente pour paquet non vérifié décrite ci-dessous — passent par un courtier de consentement partagé avec regroupement des rafales : les requêtes concurrentes pour la même portée attendent sur une seule boîte de dialogue au lieu d'empiler les alertes. Chaque invite propose les mêmes quatre décisions :
| Décision | Effet |
|---|---|
| Ne pas autoriser | Bloque par un 451 ; mémorisé pendant 60 secondes, refusant automatiquement les nouvelles tentatives immédiates. |
| Autoriser une fois | Laisse passer cette seule requête (ou rafale regroupée). |
| Autoriser pendant 15 minutes | Accorde la portée pendant 15 minutes. |
| Autoriser pour le reste de la session | Accorde la portée jusqu'à ce que l'application se ferme. |
Les autorisations et refus actifs sont listés sous Décisions de chaîne d'approvisionnement — distincts des décisions de garde-fous — dans l'interface d'approbations (la zone Fenêtre → Approbations d'identifiants…), où vous pouvez révoquer une autorisation par anticipation. Les autorisations sont uniquement en mémoire et ne survivent pas à un redémarrage de l'application.
Dans les sessions SSH/CLI distantes, il n'y a pas de boîte de dialogue graphique : la même question est rendue sous forme de sélecteur à l'intérieur du tmux de l'espace de travail. L'absence de réponse signifie refus. Voir Accès à distance.
Configurer la politique
La politique de chaîne d'approvisionnement se configure par espace de travail, dans le volet Chaîne d'approvisionnement de la fenêtre Modifier l'espace de travail (l'icône jaune de boîte d'expédition dans la barre latérale). La référence complète champ par champ se trouve dans Paramètres Chaîne d'approvisionnement ; les valeurs par défaut en un coup d'œil :
| Paramètre | Valeur par défaut |
|---|---|
| Refuser les paquets plus récents que le seuil (barrière d'ancienneté) | Activé, Âge minimum : 2 jours, aucune exemption |
| Rechercher les paquets sur api.osv.dev (gratuit, aucune clé requise) | Désactivé ; Bloquer à la gravité : Élevée et au-dessus |
| Filtrage des paquets | Aucun (pas de clé socket.dev ni Delpi) |
| Bloquer les paquets compromis / Bloquer les paquets avec des CVE connues (socket.dev) | Désactivé ; Seuil de blocage CVE : Élevée et au-dessus |
| Retirer preinstall / install / postinstall / prepare des archives npm à la volée | Désactivé, liste d'autorisation vide |
| Demander avant de transmettre les archives épinglées par lockfile sans modification | Désactivé (passe-plat silencieux) |
Trois détails opérationnels :
-
Les modifications s'appliquent en direct. Enregistrer l'espace de travail pousse immédiatement la nouvelle politique vers les sessions en cours — le proxy la lit à chaque requête, donc aucun redémarrage de la VM n'est jamais nécessaire. Chaque politique nouvelle ou modifiée est confirmée par un résumé d'une ligne dans le Journal de sécurité, par exemple :
[supply-chain] policy engaged for 1a2b3c4d: age-gate=2d osv=high socket.dev=compromised+cve=high strip-scriptsLes erreurs de configuration sont signalées directement dans ce résumé :
socket.dev=key-set-but-no-togglesignifie qu'une clé est saisie mais qu'aucun blocage n'est activé, etdelpi=selected-but-no-keysignifie que Delpi est choisi mais désactivé faute de clé. -
Où c'est stocké. La politique réside dans le
profile.jsonde l'espace de travail sous~/Library/Application Support/BromureAC/profiles/<id>/(seuls les champs non par défaut sont écrits). Les clés d'API socket.dev et Delpi y sont aussi stockées — côté hôte uniquement, affichées comme champs sécurisés dans l'interface, et jamais copiées dans la VM. Il n'y a aucune variable d'environnement ni argument de lancement propre à la chaîne d'approvisionnement. -
Configuration à distance. Le menu distant SSH/TUI expose le même volet Chaîne d'approvisionnement avec les mêmes champs, de sorte qu'une instance sans interface peut être configurée sans l'interface graphique. Voir Accès à distance.
La fenêtre du Journal de sécurité
Tout ce que fait le pipeline de chaîne d'approvisionnement est visible dans le Journal de sécurité — ouvrez Fenêtre → Journal de sécurité…. C'est un suivi en direct de chaque événement de sécurité que le proxy émet : recherches et verdicts de chaîne d'approvisionnement, blocages 451, retraits de scripts, confirmations de politique engagée et redirections Delpi, aux côtés des détections d'injection de prompt, des activations/désactivations de Fusion, des changements de routage LLM, des événements d'accès à distance et des lignes de worktree. Une seule fenêtre sert toute l'application ; choisir à nouveau l'élément de menu la ramène au premier plan.
Les lignes sont codées par couleur pour que vous puissiez lire une installation d'un coup d'œil :
| Couleur | Signification | Marqueur |
|---|---|---|
| Bleu | Recherche sortante (OSV, socket.dev, filet de sécurité horodatage de publication) | → |
| Vert | Verdict propre — le paquet a passé | ✓ |
| Rouge | Blocage ou échec (451, erreur) | ✗ |
| Orange | Scripts d'installation retirés d'une archive | « retiré » |
| Accent | Politique engagée / modifiée | [supply-chain] |
La fenêtre possède un champ Filtrer… pour restreindre la vue (tapez un nom de paquet, un écosystème, ou tout texte des lignes qui vous intéressent), une case Défilement automatique (activée par défaut ; décochez-la pour arrêter de suivre le suivi pendant que vous lisez), un bouton Effacer qui vide le tampon, et un pied de page affichant le nombre d'entrées (« 42 entries », ou « 7 of 42 entries » lorsqu'un filtre est actif). Un message d'état vide explique quand les entrées apparaîtront.
Pour le voir en action : ouvrez la fenêtre, exécutez une installation quelconque dans une session (npm install, pip install, cargo add, gem install, …), et regardez les entrées défiler. Une installation entièrement propre produit tout de même des lignes « inspection » pour chaque artefact — c'est délibéré, pour qu'un pipeline silencieux se distingue d'un pipeline contourné.
Remarque : Le tampon est un anneau en mémoire plafonné à environ 5 000 lignes et ne persiste pas d'un redémarrage à l'autre de l'application. Chaque ligne est aussi reflétée vers la sortie d'erreur standard de l'application, donc lancer
bromure-clidepuis un terminal (ou capturer sa sortie de journal) vous donne une copie durable. La fenêtre s'ouvre en 820×460 et peut être redimensionnée jusqu'à 720×360.
Comportement hors ligne et dégradé
Les vérifications de réputation du pipeline dépendent d'appels sortants depuis votre Mac (jamais depuis la VM) : api.osv.dev pour OSV, api.socket.dev pour socket.dev, pypi.org pour le filet de sécurité d'horodatage de publication PyPI, et depi-npm-proxy.landh.tech pour Delpi. Lorsqu'une source activée ne peut pas produire de verdict — réseau coupé après les tentatives, erreur HTTP, limitation de débit, échec d'authentification, ou écosystème non pris en charge — Bromure échoue en mode fermé plutôt que d'autoriser silencieusement.
Le téléchargement est suspendu et une mise en attente pour paquet non vérifié vous interroge, par paquet et par version, avec une alerte système intitulée Transmettre un paquet non vérifié depuis l'espace de travail « … » ? : Bromure n'a pas pu joindre la source pour vérifier le paquet, et l'installer signifie accepter un paquet qui n'a pas été contrôlé par rapport à vos sources de réputation configurées. Les quatre décisions de consentement standard s'appliquent ; refuser (y compris lorsqu'aucune interface graphique n'est disponible et que l'invite distante expire) bloque par un 451. Les mises en attente sont indexées par package@version, de sorte qu'une seule approbation ne peut jamais couvrir tout le graphe de dépendances.
C'est une protection délibérée : sans elle, un attaquant capable de provoquer une limitation de débit sur le service de réputation pourrait faire passer des paquets sans contrôle. Les conséquences pratiques :
- Travailler entièrement hors ligne avec OSV ou socket.dev activé signifie une invite pour chaque paquet non mis en cache. Désactivez ces couches pour les périodes hors ligne — la barrière d'ancienneté continue de fonctionner à partir des métadonnées déjà mises en cache sans connectivité.
- socket.dev plus Cargo signifie une invite pour chaque crate, puisque socket.dev ne peut pas du tout vérifier Cargo (les autorisations sont par package@version, donc une autorisation de session ne couvre que cette seule version de crate).
- Le filet de sécurité d'horodatage de publication PyPI est la seule exception : sur une pure erreur réseau, il échoue en mode ouvert pour cette récupération (l'échec n'est pas mis en cache, donc la recherche est réessayée la fois suivante). La réécriture des métadonnées de la barrière d'ancienneté n'est pas affectée.
Détection de compromission
Les vérifications de chaîne d'approvisionnement réduisent les chances que du code hostile entre dans le bac à sable ; la détection de compromission attrape le moment où du code hostile qui est entré tente d'agir. Elle fait partie du système d'identifiants plutôt que du pipeline de paquets — le tableau complet des identifiants leurres et de l'échange de jetons se trouve dans Identifiants — mais son alarme est documentée ici parce qu'un paquet empoisonné en est le déclencheur le plus probable.
Chaque requête sortante de la VM est analysée en une seule passe à la recherche des jetons leurres (« factices ») que la couche d'échange d'identifiants a frappés. Un leurre limité à un hôte (disons github.com) apparaissant dans une requête destinée à tout autre hôte est la signature d'une exfiltration d'identifiants. Lorsque cela se produit :
- Le proxy bloque la requête par un 451 — la destination ne reçoit jamais un octet.
- La VM est mise en pause instantanément. Si la session était détachée, elle est rattachée de force et révélée. Le cadre de session figé se teinte en rouge.
- Une alerte critique apparaît, intitulée Cet environnement a peut-être été compromis, expliquant que Bromure a détecté une tentative sortante de fuite d'un identifiant de session depuis l'espace de travail nommé vers un hôte pour lequel il n'a pas été frappé et que la VM a été mise en pause — avec une ligne de détail par fuite : un aperçu du jeton (comme
sk-a…f9q3), le nom de l'identifiant, l'hôte pour lequel il a été frappé, et l'hôte vers lequel il a été observé se dirigeant.
L'alerte propose trois réponses :
- Éteindre (la valeur par défaut) — arrête la VM et marque l'espace de travail comme compromis.
- Enregistrer pour investigation — vous choisissez un dossier ; Bromure exporte
disk.img(une copie du disque système de la VM),home.tar.gz(le répertoire personnel de l'espace de travail), etshares/<name>.tar.gzpour chaque dossier partagé, puis éteint et marque l'espace de travail comme compromis. L'état de la RAM est abandonné. - Continuer — accepter le risque et reprendre ; le détecteur se redéclenche si cela se reproduit. Échap et Cmd-. ne correspondent délibérément pas à Continuer — reprendre une VM possiblement hostile doit être un clic explicite.
Un espace de travail marqué comme compromis refuse de redémarrer tant que vous ne l'effacez pas explicitement, et le flux d'effacement avertit que les dossiers partagés ne sont pas effacés — ils peuvent encore contenir des fichiers contaminés, alors examinez-les à la main.
Il n'y a rien à configurer : la détection est toujours active pour les identifiants qui déclarent une portée d'hôte. Les identifiants manuels sans hôte épinglé (« tout hôte ») ne peuvent jamais la déclencher, et les jetons Claude/Codex utilisent une correspondance assouplie sur le même domaine enregistré (un jeton frappé pour api.anthropic.com vu sur un autre hôte anthropic.com ne déclenche pas l'alarme). Une seule alerte s'affiche à la fois ; les événements répétés pendant qu'elle est ouverte sont ignorés.
Visibilité en entreprise
Pour les espaces de travail inscrits auprès de bromure.io, chaque récupération de métadonnées et d'artefact émet aussi un événement supply_chain.fetch vers le flux d'événements d'entreprise, portant l'écosystème, le paquet, la version, le type de requête, le résultat (allowed, rewritten, blocked, ou stripped), et le type de raison (age_gate, osv, socket_compromised, socket_cve, verify_unavailable, lockfile_denied, scripts_stripped). L'inscription à elle seule donne aux administrateurs une visibilité sur les téléchargements de paquets à l'échelle de l'organisation — un inventaire de tout ce que chaque agent a installé — même sur les espaces de travail avec toutes les couches d'application désactivées.
Le fonctionnement de l'inscription, et ce que les administrateurs voient à l'autre bout, est couvert dans Entreprise.