Agent IA : définir ses droits et suivre ses actions
Un agent doit agir dans un périmètre explicite, avec des droits limités à sa mission. Les consignes du prompt ne remplacent pas les contrôles des services.
Article
QuickJS et WebAssembly permettent d’exécuter du JavaScript dans une JVM avec des accès limités. Les fonctions exposées et les quotas restent à contrôler.
Un agent écrit un script pour lire des données et préparer un rapport. Vous voulez l'exécuter sans lui donner accès aux fichiers du serveur, aux secrets ni à toutes les destinations réseau. QuickJS compilé en WebAssembly offre une piste pour une application Java : le script calcule dans un environnement isolé et passe par des fonctions explicitement fournies pour agir à l'extérieur.
Du code généré peut boucler, allouer trop de mémoire ou tenter un appel inattendu. Le dispositif doit limiter ces comportements et permettre d'arrêter l'exécution. Il doit aussi empêcher un script d'utiliser les identifiants ou les données d'un autre utilisateur.
Un processus ou un conteneur séparé apporte une frontière supplémentaire, avec un coût de démarrage et d'exploitation. Pour des scripts courts appelés fréquemment, une exécution dans la JVM peut être intéressante. Elle reste dans le même processus que l'application, ce qui limite l'isolation obtenue.
QuickJS est un moteur JavaScript conçu pour être embarqué. Il interprète le code du script. La famille QuickJS comprend aussi quickjs-ng ; le moteur réellement utilisé dépend de la version et de la chaîne d'intégration retenues.
Javy permet d'utiliser JavaScript avec WebAssembly. WebAssembly, souvent abrégé Wasm, est un format de code exécutable dont l'environnement contrôle les accès à l'extérieur.
Le module ne reçoit pas automatiquement le droit d'ouvrir un fichier ou une connexion réseau. Pour ces actions, l'application lui fournit des fonctions hôtes : des fonctions que le script peut appeler et dont l'implémentation appartient à l'application Java.
La bibliothèque quickjs4j relie Java et QuickJS. Un runtime WebAssembly en Java, comme endive, issu de Chicory, permet de faire fonctionner cette chaîne sans intégrer une bibliothèque native par JNI. Les versions compatibles et leurs capacités doivent être vérifiées ensemble.
Une interface de type fetch peut constituer la seule sortie réseau du script. L'application vérifie alors la destination, la méthode demandée, les autorisations et la taille des échanges avant d'envoyer la requête.
Une liste d'hôtes autorisés est un premier filtre. Il faut aussi traiter les adresses internes, la résolution DNS et les redirections selon le périmètre voulu. Refuser les redirections automatiques évite qu'une adresse autorisée renvoie discrètement vers une destination interdite.
Les délais doivent être limités : connexion, attente de réponse et durée totale de la tâche. Un plafond sur les corps reçus empêche un serveur distant de faire grossir la mémoire sans borne.
Les secrets sont ajoutés côté Java au moment de l'appel. Le script ne reçoit ni la clé ni un accès général au stockage des identifiants. L'application associe chaque secret à son utilisateur et à sa destination ; elle contrôle les en-têtes que le script peut fournir.
Le formatage des dates, le hachage ou la manipulation de texte peuvent être fournis par des fonctions locales. Ils ne nécessitent pas de réseau, mais leur consommation de ressources et les données qu'ils manipulent restent à contrôler.
Un stockage clé-valeur demande une attention particulière. S'il persiste entre les exécutions, il peut transmettre des données d'une tâche à une autre. Il faut préciser sa durée de vie, ses quotas et la séparation entre utilisateurs. Un stockage persistant n'est pas du simple calcul.
Pour la cryptographie, utilisez les primitives appropriées du runtime, notamment un générateur aléatoire adapté à la sécurité. Un résultat de Math.random() ne remplace pas une source d'aléa cryptographique.
Un plafond de mémoire et une durée maximale répondent à deux risques différents. La mémoire peut être épuisée très vite ; une boucle peut consommer du processeur sans allouer davantage. Le moteur doit pouvoir interrompre les deux cas.
Ces limites doivent couvrir les fonctions hôtes autant que possible. Un script arrêté ne doit pas laisser un appel réseau ou un traitement Java poursuivre indéfiniment son travail.
Il faut enfin limiter le nombre d'exécutions simultanées. Même des scripts individuellement bornés peuvent saturer le serveur s'ils sont trop nombreux. Une file d'attente limitée ou un refus temporaire explicite permet de protéger le reste de l'application.
Je vérifierais ces protections avec une boucle infinie, une allocation excessive, un serveur qui ne répond pas et plusieurs scripts lancés ensemble. L'objectif est de constater un arrêt contrôlé, sans bloquer le service qui les héberge.
QuickJS n'est pas à lui seul un navigateur ni Node.js. Des interfaces comme fetch, URL ou FormData doivent être fournies ou adaptées par l'environnement.
Une couche d'initialisation, parfois appelée prélude, peut installer ces fonctions avant le script. Sa documentation doit préciser les différences avec les API habituelles. Un fetch synchrone, par exemple, ne se comporte pas exactement comme celui du navigateur.
Des bibliothèques connues peuvent faciliter les calculs décimaux ou la lecture de CSV et de YAML. Leurs versions, leur chargement et leurs accès doivent rester maîtrisés. Charger seulement les bibliothèques nécessaires réduit le travail d'initialisation, à mesurer sur les scripts réels.
La chaîne JavaScript, QuickJS, WebAssembly et JVM ajoute plusieurs couches d'exécution. Une boucle JavaScript qui parcourt chaque octet d'un gros document peut devenir coûteuse, même si le téléchargement a été rapide.
Le décodage de corps, le traitement de gros fichiers ou certaines opérations cryptographiques peuvent être plus adaptés aux fonctions Java fournies par l'hôte. Le script conserve alors la coordination et les transformations légères.
Mesurez séparément le démarrage, le calcul et les appels réseau. Un temps total ne permet pas de savoir si le ralentissement vient du moteur, du script ou du service distant.
Le modèle de sécurité de WebAssembly limite les interactions du module avec son environnement. Il dépend néanmoins de la correction du runtime et des fonctions exposées. Les accès autorisés peuvent eux-mêmes être utilisés de façon indésirable.
Une exécution dans la JVM partage aussi le processus de l'application. Pour du code nécessitant des capacités système, des bibliothèques natives ou une isolation plus forte, un processus, un conteneur durci ou une autre frontière peut rester nécessaire.
Le choix repose donc sur les capacités à offrir et les conséquences d'une défaillance. Un moteur léger est utile pour des scripts bornés ; il ne justifie pas de leur ouvrir des accès que vous ne sauriez pas contrôler.
Explorer
Choisis dans l’index d’après les étiquettes de cet article — les plus proches d’abord, hors de sa série. Rien n’est écrit à la main : un article publié demain avec les mêmes étiquettes y prendra place.
Un agent doit agir dans un périmètre explicite, avec des droits limités à sa mission. Les consignes du prompt ne remplacent pas les contrôles des services.
Un modèle de langage aide à interpréter une demande floue. Pour des règles explicites, comparez-le à du code simple et mesurez le coût de ses erreurs.
Un agent combine des appels d’API dans un script au lieu de multiplier les échanges avec le modèle. Bénéfices, limites et place de MCP dans cette approche.
Laisser un commentaire
Votre commentaire sera relu avant d'être publié.