Architecture

Le moteur qui ne sait que lire et répondre

Le moteur d'analyse ne fait que deux choses : lire le modèle et répondre sur localhost. Rien d'autre — et c'est le noyau du système d'exploitation qui l'impose, pas notre code.

~6 min de lecture · Architecture

Pourquoi ce composant est le plus enfermé

Quand PoisonZero analyse un changement on-device, le moteur est le composant qui lit le texte contrôlé par l'attaquant — le memory-diff même que quelqu'un a pu concevoir pour s'évader. Nous le traitons donc comme la surface la plus exposée du système et l'enfermons en conséquence : c'est le composant le plus enfermé que nous livrons. L'objectif de conception est net : un exploit du moteur doit devenir un plantage de processus sans conséquence, pas une tête de pont. Et si le moteur plante, le daemon ne hausse pas les épaules : il annule le changement suspect (fail-closed).

Notez la séparation volontaire. Le daemon tourne privilégié à dessein — en tant que service root (LaunchDaemon / systemd) —, parce qu'il doit surveiller des fichiers de memory qui ne lui appartiennent pas et les annuler sur place ; ce travail est impossible sans privilèges. Le moteur, c'est l'exact opposé : il ne touche à rien de ce que le daemon protège, il ne lit que l'entrée suspecte, et il est donc réduit à presque rien. Cette asymétrie est l'architecture : le composant le plus exposé est le plus enfermé, et le casser n'atteint pas le daemon privilégié.

Le moteur lit, par métier, des entrées contrôlées par l'attaquant. Nous partons du principe qu'il peut être cassé — et construisons pour que le casser ne serve à rien.

macOS : la sandbox du noyau

Sous macOS, le moteur s'exécute dans la sandbox du noyau (Seatbelt / sandbox-exec) avec un profil deny-by-default : tout est interdit sauf autorisation explicite. Le profil voyage avec le daemon et réside comme un fichier auditable à côté du modèle — vous pouvez lire exactement ce que le moteur a le droit de faire. Si le service tourne en root, le moteur lui-même retombe sur un utilisateur non privilégié (nobody).

Vérifié en conditions réelles sur Apple Silicon — et la sandbox ne coûte aucune latence mesurable : une évaluation à chaud tourne en environ 1,1 seconde dans tous les cas.

Linux : Landlock, imposé par le noyau

Sous Linux, la cage est Landlock, un module de sécurité du noyau (LSM), de nouveau deny-by-default. Le moteur peut :

Les connexions sortantes sont refusées par le noyau avec « permission denied » — avant qu'un seul octet ne quitte le système. Ce n'est pas une promesse dans notre code ; c'est prouvé sur un vrai noyau en CI à chaque build. Aucune écriture sur le disque, aucun lancement de programme tiers, aucun egress réseau.

« Lire uniquement le fichier du modèle » n'est vrai que si le moteur n'a réellement besoin de rien d'autre sur le disque. C'est pourquoi le moteur Linux est compilé entièrement statiquement contre musl — sans chargeur dynamique, sans libc partagée — sur les deux architectures (amd64 et arm64). Un binaire lié normalement devrait lire et exécuter /lib64/ld-linux et la libc système au démarrage, forçant ces chemins dans l'allowlist ; le moteur statique lit exactement un fichier et exécute exactement un binaire, lui-même.

Sous Linux, le noyau refuse la connexion sortante du moteur avant qu'un octet ne quitte la machine. La garantie vit sous notre code — là où l'entrée de l'attaquant ne peut l'atteindre.

Windows : jeton restreint + Job Object

Sous Windows, le moteur tourne sous un jeton restreint avec un Job Object du noyau — et c'est en production et vérifié, fail-closed exactement comme macOS et Linux. Le jeton est créé de sorte que tous les privilèges sont retirés (seul subsiste l'inoffensif privilège « notifier au changement »), et le processus tourne en intégrité basse (low integrity), donc il ne peut écrire d'objets à intégrité normale. Le Job Object laisse le moteur exécuter exactement un processus — il ne peut engendrer aucun enfant, le noyau le bloque — et plafonne sa mémoire. Il se lie toujours à localhost uniquement, sans connexion sortante.

Vérifié en conditions réelles sur GitHub Actions windows-latest : le lancement de processus enfants est bloqué par le noyau, et le vrai moteur Windows charge et évalue correctement sous les contraintes de jeton et de Job. (Une étape encore plus stricte avec AppContainer est sur la feuille de route ; le profil actuel est déjà livré, en production et fail-closed.)

Strictement fail-closed : pas de sandbox, pas d'analyse

Sur toutes les plateformes, la règle est la même et elle est stricte : si la sandbox n'est pas disponible, le moteur ne démarre pas. Il n'y a pas de dégradation silencieuse vers une exécution sans sandbox. À la place, les changements suspects sont annulés plutôt que laissés passer sans évaluation. L'analyse on-device sans la cage du noyau n'a tout simplement pas lieu.

C'est une limite honnête — et c'est tout l'intérêt. « Fail-closed » signifie : sans Landlock (ou Seatbelt), il n'y a pas d'analyse on-device sur cet hôte. Nous préférons vous protéger en annulant plutôt que d'exposer le composant le plus exposé à l'attaquant sans sa cage. La limite est la fonctionnalité.

Ce qu'il demande à vos machines

macOS : rien de particulier — la sandbox du noyau est toujours présente.

Linux : un noyau ≥ 5.13 avec Landlock actif. Cela couvre Ubuntu ≥ 22.04, Debian ≥ 12 et RHEL ≥ 9.6 d'origine ; sur d'autres distributions, vous l'activez avec le paramètre de démarrage lsm=landlock,…. Dans les conteneurs, le profil seccomp par défaut de Docker bloque les appels système Landlock — il faut alors un profil adapté.

Windows : rien de particulier — le jeton restreint et le Job Object sont des fonctions standard du noyau.

Pour les environnements où Landlock ne peut être activé, il existe une alternative opt-in : l'évaluation dans le cloud comme filet de sécurité distant. La protection reste en place ; le flux de données est documenté de façon transparente plutôt que caché.

En bref

La conception complète, y compris le contrat d'egress vérifiable, figure dans le livre blanc technique. À lire aussi : Pourquoi le fail-closed l'emporte et Memory Poisoning.

Une analyse qu'on ne peut retourner contre vous.

PoisonZero exécute le moteur on-device dans une cage imposée par le noyau — lire le modèle, répondre sur localhost, rien d'autre.

Sign me up

À lire aussi : Pourquoi le Fail-closed l'emporte · Qu'est-ce que le Memory Poisoning ? · Claude, MCP & tool poisoning

Tous les articles