
Sandbox zero-trust pour agents IA avec un jail de système de fichiers au niveau noyau, un proxy réseau transparent, et un moteur de politiques basé sur YAML pour intercepter et contrôler les commandes shell, les opérations sur fichiers, et les requêtes réseau.
Sandbox Zero Trust pour agents IA autonomes.
AgentGuard enveloppe tout agent IA (LangChain, CrewAI, AutoGen, scripts personnalisés) avec des rails de sécurité. Un simple changement de commande :
# Avant (dangereux — l'agent a un accès complet au système)
python my_agent.py
# Après (en bac à sable)
agentguard run -- python my_agent.py
AgentGuard intercepte chaque commande shell, modification de fichier et requête réseau effectuée par l'agent. Les actions sûres sont automatiquement autorisées, les actions dangereuses sont automatiquement bloquées, et tout le reste invite l'humain à approuver.
AgentGuard dispose de quatre couches de défense qui fonctionnent ensemble :
┌─────────────────────────────────────────────────────────────┐
│ Couche 0 : Prison de fichiers (sandbox-exec sur macOS) │
│ Application au niveau du noyau. Restreint les écritures │
│ de fichiers et le réseau au niveau des appels système. │
│ L'agent ne peut pas contourner depuis l'espace utilisateur.│
│ Bloque open() de Python, requests.post(), etc. │
├─────────────────────────────────────────────────────────────┤
│ Couche 1 : Proxy réseau │
│ Proxy HTTP/HTTPS transparent. Chaque appel réseau de │
│ l'agent est vérifié par rapport à la politique. │
│ Autorisation/refus par destination avec visibilité totale │
│ dans la TUI. │
├─────────────────────────────────────────────────────────────┤
│ Couche 2 : Shims de PATH │
│ Scripts shell shim qui interceptent des commandes comme │
│ git, pip, curl, rm. Chaque shim demande la permission au │
│ démon avant d'exécuter le vrai binaire. │
├─────────────────────────────────────────────────────────────┤
│ Couche 3 : Moteur de politique + Démon d'approbation │
│ Règles basées sur YAML évaluant chaque action interceptée.│
│ Autorise automatiquement les commandes sûres, bloque │
│ automatiquement les dangereuses, invite l'humain pour │
│ tout le reste. │
└─────────────────────────────────────────────────────────────┘
Aucune couche unique ne constitue la limite de sécurité. Elles travaillent ensemble — défense en profondeur.
go build -o agentguard ./cmd/agentguard/
go build -o agentguard-check ./cmd/agentguard-check/
Les deux binaires doivent être dans le même répertoire.
agentguard init
Ceci crée .agentguard/policy.yaml dans le répertoire courant. Modifiez-le selon vos besoins.
agentguard run -- python my_agent.py
La TUI prend le contrôle du terminal et affiche :
Pour les outils interactifs comme Claude Code qui ont besoin du terminal :
agentguard run --headless -- claude
L'agent obtient le terminal directement. AgentGuard s'exécute silencieusement en arrière-plan. Tous les événements sont enregistrés dans ~/.agentguard/logs/headless.log. Surveillez dans un autre terminal :
tail -f ~/.agentguard/logs/headless.log
agentguard run [flags] -- <command> [args...]
--policy <path> Utiliser un fichier de politique spécifique
--headless Pas de TUI — l'agent obtient le terminal
--default-allow Autoriser automatiquement les décisions PROMPT en mode headless (défaut : auto-deny)
--no-sandbox Désactiver sandbox-exec (shims et proxy toujours actifs)
agentguard init Créer un fichier de politique par défaut
agentguard version Afficher la version
Les politiques sont des fichiers YAML qui définissent ce que l'agent peut et ne peut pas faire. AgentGuard vérifie trois emplacements (dans l'ordre) :
./.agentguard/policy.yaml (local au projet)~/.agentguard/policy.yaml (global utilisateur)version: 1
deny:
# Bloquer les commandes dangereuses
- command: "rm"
args: "-rf *"
reason: "La suppression forcée récursive est trop dangereuse"
- command: "sudo"
args: "*"
reason: "L'escalade de privilèges n'est pas autorisée"
- command: "chmod"
args: "777 *"
reason: "Les permissions en écriture mondiale sont dangereuses"
# Bloquer la lecture de fichiers sensibles (appliqué par sandbox-exec)
- file:
path: "*.env"
action: "read"
reason: "Ne pas laisser l'agent lire les fichiers .env"
- file:
path: "*.pem"
action: "read"
reason: "Ne pas laisser l'agent lire les clés privées"
allow:
# Commandes en lecture seule sûres
- command: "ls"
- command: "cat"
- command: "pwd"
- command: "echo"
- command: "grep"
- command: "head"
- command: "tail"
- command: "wc"
# Git en lecture seule
- command: "git"
args: "status"
- command: "git"
args: "log *"
- command: "git"
args: "diff *"
# Autoriser les écritures dans l'espace de travail
- file:
path: "/tmp/workspace/**"
action: "write"
# Autoriser des endpoints API spécifiques
- network:
destination: "api.anthropic.com:443"
- network:
destination: "api.github.com:443"
deny network *) — vérifiées en troisième. Agit comme un refus par défaut.Règles de commande — correspondent aux commandes shell par nom et motif d'argument :
- command: "git"
args: "push *"
reason: "Le push nécessite une approbation"
Règles de fichier — correspondent aux opérations sur les fichiers (appliquées par sandbox-exec) :
- file:
path: "*.env"
action: "read" # "read" ou "write"
reason: "Protéger les secrets"
Règles réseau — correspondent aux destinations réseau (appliquées par proxy + sandbox-exec) :
- network:
destination: "api.anthropic.com:443"
Utilisez * comme caractère générique dans les arguments de commande, les chemins de fichiers et les destinations réseau.
| Touche | Action | Quand |
|---|---|---|
Y | Autoriser la requête en attente | Invite d'approbation visible |
N | Refuser la requête en attente | Invite d'approbation visible |
A | Autoriser + mémoriser pour cette session ("Toujours autoriser") | Invite d'approbation visible |
B | Refuser + mémoriser pour cette session ("Bloquer pour toujours") | Invite d'approbation visible |
Tab | Basculer le panneau stdout/stderr de l'agent | Toujours |
Haut/Bas | Faire défiler le flux d'activité | Toujours |
Q | Quitter (tue l'agent) | Toujours |
| Action de l'agent | Shims | Proxy | sandbox-exec |
|---|---|---|---|
subprocess.run(["rm", "-rf", "/"]) | Oui | - | - |
subprocess.run(["git", "push"]) | Oui | - | - |
requests.post("https://evil.com") | - | Oui | Oui |
urllib.request.urlopen("https://api.com") | - | Oui | Oui |
open(".env", "r") | - | - | Oui |
open("/etc/shadow", "w") | - | - | Oui |
/usr/bin/curl https://evil.com (chemin absolu) | - | Oui | Oui |