Retour aux mises à jour
New releaseSep 12, 2026

sandbox-runtime v0.0.76

Un outil de sandboxing léger pour appliquer des restrictions de système de fichiers et de réseau à des processus arbitraires au niveau du système d'exploitation, sans nécessiter de conteneur.

Partager

Anthropic Sandbox Runtime (srt)

Un outil de sandboxing léger pour appliquer des restrictions de système de fichiers et de réseau à des processus arbitraires au niveau du système d'exploitation, sans nécessiter de conteneur.

srt utilise les primitives natives de sandboxing du système d'exploitation (sandbox-exec sur macOS, bubblewrap sur Linux) et un filtrage réseau basé sur un proxy. Il peut être utilisé pour sandboxer le comportement d'agents, de serveurs MCP locaux, de commandes bash et de processus arbitraires.

Aperçu de recherche bêta

Le Sandbox Runtime est un aperçu de recherche développé pour Claude Code afin de permettre des agents IA plus sûrs. Il est mis à disposition en tant qu'aperçu open source précoce pour aider l'écosystème au sens large à construire des systèmes agentiques plus sécurisés. Comme il s'agit d'un aperçu de recherche précoce, les API et les formats de configuration peuvent évoluer. Nous accueillons favorablement les retours et les contributions pour rendre les agents IA plus sûrs par défaut !

Installation```bash

npm install -g @anthropic-ai/sandbox-runtime

## Utilisation de base```bash
# Network restrictions
$ srt "curl anthropic.com"
Running: curl anthropic.com
<html>...</html>  # Request succeeds

$ srt "curl example.com"
Running: curl example.com
Connection blocked by network allowlist  # Request blocked

# Filesystem restrictions
$ srt "cat README.md"
Running: cat README.md
# Anthropic Sandb...  # Current directory access allowed

$ srt "cat ~/.ssh/id_rsa"
Running: cat ~/.ssh/id_rsa
cat: /Users/ollie/.ssh/id_rsa: Operation not permitted  # Specific file blocked

Aperçu

Ce paquet fournit une implémentation de sandbox autonome qui peut être utilisée à la fois comme outil CLI et comme bibliothèque. Il est conçu selon une philosophie sécurisée par défaut adaptée aux cas d'usage courants des développeurs : les processus démarrent avec un accès minimal, et vous n'ouvrez explicitement que les brèches dont vous avez besoin.

Capacités principales :

  • Restrictions réseau : contrôlez quels hôtes/domaines peuvent être accédés via HTTP/HTTPS et d'autres protocoles
  • Restrictions du système de fichiers : contrôlez quels fichiers/répertoires peuvent être lus/écrits
  • Restrictions des sockets Unix : contrôlez l'accès aux sockets IPC locaux
  • Surveillance des violations : sur macOS, accédez au magasin de journaux de violations du sandbox du système pour des alertes en temps réel

Cas d'usage exemple : sandboxer les serveurs MCP

Un cas d'usage clé consiste à sandboxer les serveurs Model Context Protocol (MCP) afin de restreindre leurs capacités. Par exemple, pour sandboxer le serveur MCP du système de fichiers :

Sans sandboxing (.mcp.json) :```json { "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem"] } } }

**Avec sandboxing** (`.mcp.json`) :```json
{
  "mcpServers": {
    "filesystem": {
      "command": "srt",
      "args": ["npx", "-y", "@modelcontextprotocol/server-filesystem"]
    }
  }
}

Ensuite, configurez les restrictions dans ~/.srt-settings.json :```json { "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": ["~/sensitive-folder"] }, "network": { "allowedDomains": [], "deniedDomains": [] } }

Désormais, le serveur MCP sera bloqué en écriture vers le chemin refusé :```
> Write a file to ~/sensitive-folder
✗ Error: EPERM: operation not permitted, open '/Users/ollie/sensitive-folder/test.txt'

Fonctionnement

Le sandbox utilise des primitives au niveau du système d'exploitation pour appliquer des restrictions qui s'appliquent à l'ensemble de l'arborescence des processus :

  • macOS : Utilise sandbox-exec avec des profils Seatbelt générés dynamiquement
  • Linux : Utilise bubblewrap pour la conteneurisation avec isolation du namespace réseau
  • Windows : Exécute le processus sandboxé sous un compte utilisateur local dédié srt-sandbox, avec une clôture de sortie Windows Filtering Platform basée sur le SID de ce compte et des ACE explicites par session sur l'arborescence de travail

0d1c612947c798aef48e6ab4beb7e8544da9d41a-4096x2305

Modèle d'isolation double

L'isolation du système de fichiers et du réseau sont toutes deux nécessaires pour un sandboxing efficace. Sans isolation des fichiers, un processus compromis pourrait exfiltrer des clés SSH ou d'autres fichiers sensibles. Sans isolation réseau, un processus pourrait s'échapper du sandbox et obtenir un accès réseau non restreint.

Isolation du système de fichiers applique des restrictions de lecture et d'écriture :

  • Lecture (modèle refuser-puis-autoriser) : Par défaut, l'accès en lecture est autorisé partout. Vous pouvez refuser de larges régions (par exemple, /Users) puis réautoriser des chemins spécifiques à l'intérieur de celles-ci (par exemple, .). allowRead a la priorité sur denyRead — l'inverse de l'écriture, où denyWrite a la priorité sur allowWrite. Une entrée denyRead plus spécifique que la région allowRead dans laquelle elle se trouve (par exemple denyRead: ["**/.env"] ou ["./secrets"] avec allowRead: ["."]) reste refusée.
  • Écriture (modèle autoriser-uniquement) : Par défaut, l'accès en écriture est refusé partout. Vous devez explicitement autoriser des chemins (par exemple, ., /tmp). Une liste d'autorisation vide signifie aucun accès en écriture.

Isolation réseau (modèle autoriser-uniquement) : Par défaut, tout accès réseau est refusé. Vous devez explicitement autoriser des domaines. Une liste allowedDomains vide signifie aucun accès réseau. Le trafic réseau est acheminé via des serveurs proxy s'exécutant sur l'hôte :

  • Linux : Les requêtes sont acheminées via le système de fichiers sur un socket de domaine Unix. Le namespace réseau du processus sandboxé est entièrement supprimé, donc tout le trafic réseau doit passer par les proxies s'exécutant sur l'hôte (écoutant sur des sockets Unix qui sont montés par liaison dans le sandbox)

  • macOS : Le profil Seatbelt autorise la communication uniquement vers un port localhost spécifique. Les proxies écoutent sur ce port, créant un canal contrôlé pour tout accès réseau

  • Windows : Un ensemble de filtres WFP à l'échelle de la machine bloque toutes les connexions sortantes provenant du compte srt-sandbox sauf le loopback vers la plage de ports proxy. Les proxies écoutent dans cette plage, créant un canal contrôlé pour tout accès réseau

Tant le trafic HTTP/HTTPS (via proxy HTTP) que les autres trafics TCP (via proxy SOCKS5) sont médiés par ces proxies, qui appliquent vos listes d'autorisation et de refus de domaines.

Pour plus de détails sur le sandboxing dans Claude Code, voir :

Architecture```

src/ ├── index.ts # Library exports ├── cli.ts # CLI entrypoint (srt command) ├── utils/ # Shared utilities │ ├── debug.ts # Debug logging │ ├── settings.ts # Settings reader (permissions + sandbox config) │ ├── platform.ts # Platform detection │ └── exec.ts # Command execution utilities └── sandbox/ # Sandbox implementation ├── sandbox-manager.ts # Main sandbox manager ├── sandbox-schemas.ts # Zod schemas for validation ├── sandbox-violation-store.ts # Violation tracking ├── sandbox-utils.ts # Shared sandbox utilities ├── http-proxy.ts # HTTP/HTTPS proxy for network filtering ├── socks-proxy.ts # SOCKS5 proxy for network filtering ├── linux-sandbox-utils.ts # Linux bubblewrap sandboxing ├── macos-sandbox-utils.ts # macOS sandbox-exec sandboxing └── windows-sandbox-utils.ts # Windows srt-win sandboxing

## Utilisation

### En tant qu'outil CLI

La commande `srt` (Anthropic Sandbox Runtime) encapsule n'importe quelle commande avec des limites de sécurité :```bash
# Run a command in the sandbox
srt echo "hello world"

# With debug logging
srt --debug curl https://example.com

# Specify custom settings file
srt --settings /path/to/srt-settings.json npm install

En tant que bibliothèque```typescript

import { SandboxManager, type SandboxRuntimeConfig, } from '@anthropic-ai/sandbox-runtime' import { spawn } from 'child_process'

// Define your sandbox configuration const config: SandboxRuntimeConfig = { network: { allowedDomains: ['example.com', 'api.github.com'], deniedDomains: [], }, filesystem: { denyRead: ['~/.ssh'], allowWrite: ['.', '/tmp'], denyWrite: ['.env'], }, }

// Initialize the sandbox (starts proxy servers, etc.) await SandboxManager.initialize(config)

// Wrap a command with sandbox restrictions const sandboxedCommand = await SandboxManager.wrapWithSandbox( 'curl https://example.com', )

// Execute the sandboxed command const child = spawn(sandboxedCommand, { shell: true, stdio: 'inherit' })

// Handle exit and cleanup after child process completes child.on('exit', async code => { console.log(Command exited with code ${code}) // Cleanup when done (optional, happens automatically on process exit) await SandboxManager.reset() })

**Attribution des violations (`commandId` / `commandText`).** Les violations observées pendant l'exécution d'une commande encapsulée (lignes de journal seatbelt, événements seccomp, refus du proxy) sont stockées sous une clé d'attribution, et `annotateStderrWithSandboxFailures(key, stderr)` / `getViolationsForCommand(key)` les récupèrent via cette même clé. Par défaut, la clé est la chaîne encapsulée elle-même. Passez un `commandId` opaque par invocation (par ex. un id d'utilisation d'outil) pour utiliser celui-ci comme clé à la place — recommandé : les clés sont comparées sur leurs 100 premiers caractères, donc de longues commandes partageant un préfixe s'attribueraient mutuellement leurs violations, et une réexécution du même texte hériterait des événements de l'exécution précédente. Si la chaîne que vous *exécutez* n'est pas la commande que l'invocation *représente* (par ex. vous encapsulez un `source <snapshot> && eval '<cmd>'` assemblé), passez aussi `commandText: '<cmd>'` : c'est ce à quoi les motifs de commande de `ignoreViolations` sont comparés et ce que chaque violation rapporte comme son `command`.```typescript
const wrapped = await SandboxManager.wrapWithSandbox(
  assembledCommand, // what actually runs
  undefined,
  undefined,
  undefined,
  { commandId: invocationId, commandText: rawCommand },
)
// ... run it ...
const annotated = SandboxManager.annotateStderrWithSandboxFailures(invocationId, stderr)

Exports disponibles```typescript

// Main sandbox manager export { SandboxManager } from '@anthropic-ai/sandbox-runtime'

// Violation tracking export { SandboxViolationStore } from '@anthropic-ai/sandbox-runtime'

// TypeScript types export type { SandboxRuntimeConfig, NetworkConfig, FilesystemConfig, IgnoreViolationsConfig, SandboxAskCallback, FsReadRestrictionConfig, FsWriteRestrictionConfig, NetworkRestrictionConfig, } from '@anthropic-ai/sandbox-runtime'

## Configuration

### Emplacement du fichier de paramètres

Par défaut, le runtime du sandbox recherche la configuration à l'emplacement `~/.srt-settings.json`. Vous pouvez spécifier un chemin personnalisé à l'aide du flag `--settings` :```bash
srt --settings /path/to/srt-settings.json <command>

Exemple de configuration complet```json

{ "network": { "allowedDomains": [ "github.com", ".github.com", "lfs.github.com", "api.github.com", "npmjs.org", ".npmjs.org" ], "deniedDomains": ["malicious.com"], "allowUnixSockets": ["/var/run/docker.sock"], "allowLocalBinding": false }, "filesystem": { "denyRead": ["~/.ssh"], "allowRead": [], "allowWrite": [".", "src/", "test/", "/tmp"], "denyWrite": [".env", "config/production.json"] }, "ignoreViolations": { "*": ["/usr/bin", "/System"], "git push": ["/usr/bin/nc"], "npm": ["/private/tmp"] }, "enableWeakerNestedSandbox": false, "enableWeakerNetworkIsolation": false, "allowAppleEvents": false }

### Options de configuration

#### Configuration réseau

Utilise un **modèle allow-only** - tout accès réseau est refusé par défaut.

- `network.allowedDomains` - Tableau de domaines autorisés (prend en charge les jokers comme `*.example.com`). Tableau vide = aucun accès réseau. Un suffixe `:port` optionnel (`api.example.com:443`, `*.example.com:8443`) restreint une entrée à ce port de destination ; les entrées sans port correspondent à n'importe quel port.
  - Les littéraux IPv6 doivent être entre crochets, à la manière RFC 3986 : `[::1]`, `[2001:db8::1]:443`. Une entrée multi-deux-points sans crochets est rejetée comme ambiguë (`2001:db8::1:443` est lui-même une adresse valide).
- `network.deniedDomains` - Tableau de domaines refusés (vérifiés en premier, prioritaires sur allowedDomains). Même suffixe `:port`, et un `*` nu (ou `*:22`) est accepté pour tout refuser.
- `network.deniedDomainReasons` - Map optionnelle d'une entrée `deniedDomains` (correspondance par chaîne exacte) vers une raison destinée au modèle qui apparaît dans la ligne `<sandbox_violations>` lorsque cette entrée bloque une connexion — indiquez ce qui est bloqué et l'alternative autorisée (par ex. `{"github.com:22": "SSH pushes to GitHub are blocked; use an https:// remote"}`). Les entrées sans raison rapportent une raison générique. Pour les destinations SSH (port 22), la raison est également transmise en bande : un client SSH tunnelisé via un ProxyCommand SOCKS sans authentification (par ex. BSD `nc -X 5`) reçoit une déconnexion SSH pré-échange de clés dont la description est la raison, qu'OpenSSH imprime textuellement — gardez ces raisons sous ~400 caractères ASCII, à l'impératif en premier, car OpenSSH tronque et échappe les caractères non-ASCII.
- `network.allowLocalBinding` - Autoriser la liaison à des ports locaux (booléen, par défaut : false)

**Vérification des adresses résolues.** Les listes d'autorisation/refus correspondent par _nom_, mais quiconque contrôle le DNS d'un nom autorisé (ou de n'importe quel label sous un joker autorisé) contrôle ce vers quoi il résout. Donc avant de composer directement un **hostname** autorisé, le proxy le résout une fois, écarte toute adresse dans un ensemble refusé, et se connecte à une adresse survivante (l'adresse qui a passé la vérification est celle composée — il n'y a pas de seconde résolution). Si rien ne survit, la connexion est refusée comme n'importe quel autre refus de politique : HTTP/CONNECT reçoivent `403` (`X-Proxy-Error: blocked-by-sandbox-runtime`, la raison dans le corps), SOCKS reçoit "connection not allowed by ruleset", et une ligne `deny network-outbound host:port (resolved to a loopback address)` — nommant la classe d'adresse (loopback, link-local, celle de cet hôte, métadonnées cloud, sur liste de refus, listée, …), pas l'adresse elle-même, que seul le journal de débogage porte — est enregistrée dans le magasin de violations.

L'ensemble refusé est : loopback (`127.0.0.0/8`, `::1`), non spécifié (`0.0.0.0/8`, `::`), link-local (`169.254.0.0/16`, `fe80::/10`), multicast (`224.0.0.0/4`, `ff00::/8`), broadcast, les points de terminaison de métadonnées d'instance cloud / de plateforme qui vivent hors du link-local (`100.100.100.200`, `168.63.129.16`, `192.0.0.192`, `fd00:ec2::/32`, `fd20:ce::254`, `fd00:c1::a9fe:a9fe`, `fd00:42::42`), chaque adresse actuellement assignée à l'une des interfaces réseau de cet hôte lui-même (un service lié à `0.0.0.0` répond sur l'adresse LAN ou globale exactement comme il le fait sur loopback), chaque littéral IP listé dans `deniedDomains` (en honorant son `:port` s'il en a un), et tout ce qui se trouve dans `deniedResolvedAddresses`. Les entrées IPv4 correspondent aussi aux formes IPv6 qui portent une adresse IPv4 — les adresses IPv4-mapped, IPv4-compatible et IPv4-translated, le préfixe bien connu NAT64 (`64:ff9b::/96`) et 6to4 (`2002::/16`) sont jugées par l'adresse IPv4 qu'elles intègrent. Le préfixe NAT64 à usage local `64:ff9b:1::/48` et les préfixes spécifiques au réseau ne sont pas décodés — leur disposition (la RFC 6052 autorise l'IPv4 à plusieurs positions) ne peut pas être reconnue à partir de la seule adresse ; sur un tel réseau, listez les traductions du préfixe des plages que vous refusez (par ex. `<prefix>::a00:0/104` pour `10.0.0.0/8`). Les adresses qui atteignent cet hôte sans lui être assignées — l'adresse publique 1:1-NAT d'une instance cloud, un port-forward de routeur, un alias de passerelle hôte de conteneur ou de VM — ne sont pas couvertes automatiquement ; listez-les dans `deniedResolvedAddresses`.

Ce que la vérification laisse tranquille : les entrées de liste d'autorisation qui **sont** des littéraux IP (autoriser `127.0.0.1:3000` est un choix explicite) — et, par la même logique, un hostname peut résoudre vers une adresse autrement refusée lorsque ce littéral IP (sur ce port) est lui-même dans `allowedDomains`, puisque l'atteindre par nom n'accorde rien que l'entrée littérale n'accorde pas (un littéral IP dans `deniedDomains` l'emporte toujours, exactement comme pour une requête littérale). Ainsi une configuration de développement où `myapp.test` pointe vers un serveur local via `/etc/hosts` autorise `["myapp.test", "127.0.0.1:3000"]` ; il n'y a pas de liste d'exception séparée. `localhost` et les noms sous `.localhost` résolvent vers loopback (ou un littéral autorisé) et rien d'autre. La vérification n'est pas évaluée pour les connexions routées via `parentProxy` (y compris celui repris de `HTTP_PROXY` / `HTTPS_PROXY` dans l'environnement propre de srt) ou `mitmProxy` — ce saut résout le nom et possède sa propre politique d'adresses — et elle ne régit que ce que le proxy compose : sur macOS, `allowLocalBinding` permet séparément au processus sandboxé de se connecter à des ports loopback sans passer du tout par le proxy.

- `network.deniedResolvedAddresses` - Adresses IP / plages CIDR supplémentaires (IPv4 ou IPv6, sans crochets, n'importe quel port) vers lesquelles les hostnames autorisés ne doivent pas résoudre. L'espace à usage privé n'est pas refusé par défaut car autoriser un hostname d'intranet est légitime ; listez-le ici lorsque les noms autorisés doivent en rester exclus, par ex. `["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/10", "fc00::/7"]`. Listez les plages IPv4 et IPv6 séparément — une plage IPv6 assez large pour couvrir le bloc IPv4-mapped (`::ffff:0:0/96`), comme `::/0`, correspond aux réponses IPv4 sur certains runtimes mais pas d'autres, donc ne comptez pas sur elle pour refuser l'IPv4.

**Terminaison TLS** (`network.tlsTerminate`, expérimental) : lorsqu'elle est définie, les CONNECT HTTPS sont terminés dans le processus afin que SRT puisse voir (et filtrer, via `network.filterRequest`) les requêtes déchiffrées. Le processus sandboxé est pointé vers un bundle de confiance contenant la CA MITM (`caCertPath`/`caKeyPath`, ou une CA éphémère si omis) plus les racines habituelles de l'hôte, afin que les certificats émis par le proxy et les certificats amont réels se vérifient tous deux.

- `network.tlsTerminate.excludeDomains` - Motifs de domaine (même syntaxe que `allowedDomains`) qui ne sont **pas** terminés. Les CONNECT correspondants sont tunnelisés de manière opaque à la place : ils restent soumis à la liste d'autorisation de domaines, mais le client à l'intérieur du sandbox effectue son propre handshake TLS avec l'amont réel, et `filterRequest` / l'injection d'identifiants ne s'appliquent pas à leur trafic HTTPS. Utilisez ceci pour les deux cas que la terminaison TLS casse fondamentalement :
  - **Amonts mTLS** - seul le client dans le sandbox détient le certificat client, donc le proxy ne peut pas réémettre la connexion en son nom.
  - **Clients à épinglage de certificat** - clients qui vérifient eux-mêmes l'identité de l'amont (CA personnalisées, épinglage SAN) et rejettent le certificat MITM.
- `network.tlsTerminate.extraCaCertPaths` - Chemins vers des fichiers de certificats CA PEM ajoutés à ce bundle de confiance, après la CA MITM et les racines habituelles de l'hôte. Les hôtes exclus (non terminés) sont vérifiés par le client à l'intérieur du sandbox, et les variables d'environnement de confiance que SRT définit (`SSL_CERT_FILE`, `GIT_SSL_CAINFO`, ...) _remplacent_ la configuration de confiance propre à chaque outil, donc une racine locale au site (par ex. une CA mTLS interne) doit être dans le bundle sinon ces hôtes ne pourront jamais être vérifiés. Seuls les blocs `CERTIFICATE` de chaque fichier sont copiés dans le bundle (tout le reste, par ex. une clé privée dans un PEM combiné, n'est jamais exposé au sandbox) ; les fichiers manquants, illisibles, ou ne contenant aucun bloc `CERTIFICATE` PEM sont ignorés, il est donc sûr de lister des chemins qui n'existent que sur certains hôtes.```json
{
  "network": {
    "allowedDomains": ["*.example.com", "internal-mtls.example.net"],
    "deniedDomains": [],
    "tlsTerminate": {
      "excludeDomains": ["internal-mtls.example.net"],
      "extraCaCertPaths": ["/etc/internal-mtls-roots.pem"]
    }
  }
}

Paramètres de socket Unix (comportement spécifique à la plateforme) :

ParamètremacOSLinux
allowUnixSockets: string[]Liste d'autorisation des chemins de socketsIgnoré (seccomp ne peut pas filtrer par chemin)
allowAllUnixSockets: booleanAutoriser tous les socketsDésactiver le blocage seccomp

Les sockets Unix sont bloqués par défaut sur les deux plateformes.

  • macOS : Utilisez allowUnixSockets pour autoriser des chemins spécifiques (par exemple, ["/var/run/docker.sock"]), ou allowAllUnixSockets: true pour tout autoriser.
  • Linux : Le blocage utilise des filtres seccomp (x64/arm64 uniquement). Si seccomp n'est pas disponible, les sockets sont non restreints et un avertissement est affiché. Utilisez allowAllUnixSockets: true pour désactiver explicitement le blocage.

Configuration du système de fichiers

Utilise deux modèles différents :

Restrictions de lecture (modèle refuser-puis-autoriser) - toutes les lectures sont autorisées par défaut :

  • filesystem.denyRead - Tableau de chemins dont l'accès en lecture est refusé. Tableau vide = accès complet en lecture.
  • filesystem.allowRead - Tableau de chemins dont l'accès en lecture est ré-autorisé dans les régions refusées (a la priorité sur denyRead). Remarque : c'est l'inverse de l'écriture, où denyWrite a la priorité sur allowWrite.

Restrictions d'écriture (modèle autoriser-uniquement) - toutes les écritures sont refusées par défaut :

  • filesystem.allowWrite - Tableau de chemins dont l'accès en écriture est autorisé. Tableau vide = aucun accès en écriture.
  • filesystem.denyWrite - Tableau de chemins dont l'accès en écriture est refusé dans les chemins autorisés (a la priorité sur allowWrite)

Syntaxe des chemins (macOS) :

Les chemins prennent en charge les motifs glob de style git sur macOS, similaires à la syntaxe .gitignore :

  • * - Correspond à n'importe quel caractère sauf / (par exemple, *.ts correspond à foo.ts mais pas à foo/bar.ts)
  • ** - Correspond à n'importe quel caractère, y compris / (par exemple, src/**/*.ts correspond à tous les fichiers .ts dans src/)
  • ? - Correspond à n'importe quel caractère unique sauf / (par exemple, file?.txt correspond à file1.txt)
  • [abc] - Correspond à n'importe quel caractère de l'ensemble (par exemple, file[0-9].txt correspond à file3.txt)

Exemples :

  • "allowWrite": ["src/"] - Autoriser l'écriture dans tout le répertoire src/
  • "allowWrite": ["src/**/*.ts"] - Autoriser l'écriture dans tous les fichiers .ts dans src/ et ses sous-répertoires
  • "denyRead": ["~/.ssh"] - Refuser la lecture du répertoire SSH
  • "denyRead": ["/Users"], "allowRead": ["."] - Refuser la lecture de tout /Users, mais ré-autoriser le répertoire courant
  • "denyWrite": [".env"] - Refuser l'écriture dans le fichier .env (même si le répertoire courant est autorisé)

Syntaxe des chemins (Linux) :

Linux ne prend actuellement pas en charge la correspondance glob. Utilisez uniquement des chemins littéraux :

  • "allowWrite": ["src/"] - Autoriser l'écriture dans le répertoire src/
  • "denyRead": ["/home/user/.ssh"] - Refuser la lecture du répertoire SSH
  • "denyRead": ["/home"], "allowRead": ["."] - Refuser la lecture de tout /home, mais ré-autoriser le répertoire courant

Toutes les plateformes :

  • Les chemins peuvent être absolus (par exemple, /home/user/.ssh) ou relatifs au répertoire de travail courant (par exemple, ./src)
  • ~ se développe en répertoire personnel de l'utilisateur

Autres configurations

  • ignoreViolations - Objet associant des motifs de commandes à des tableaux de chemins où les violations doivent être ignorées
  • enableWeakerNestedSandbox - Activer le mode sandbox affaibli pour les environnements Docker (booléen, par défaut : false)
  • javaAgentJarPath - macOS/Linux : chemin absolu vers srt-proxy-agent.jar, l'agent JVM injecté via JAVA_TOOL_OPTIONS (voir « Outils JVM » sous Isolation réseau). Nécessaire uniquement pour les consommateurs qui intègrent sandbox-runtime et livrent le jar séparément ; une installation npm normale le trouve sous vendor/java-proxy-agent/.
  • enableWeakerNetworkIsolation - Autoriser l'accès à com.apple.trustd.agent dans le sandbox macOS (booléen, par défaut : false). Ceci est nécessaire pour que les programmes Go (gh, gcloud, terraform, kubectl, etc.) puissent vérifier les certificats TLS lors de l'utilisation de httpProxyPort avec un proxy MITM et une CA personnalisée. Avertissement de sécurité : activer cette option ouvre un vecteur potentiel d'exfiltration de données via le service trustd.
  • allowAppleEvents - Autoriser l'envoi d'Apple Events et de requêtes d'ouverture Launch Services depuis le sandbox macOS (booléen, par défaut : false). Sans cela, des commandes comme open, osascript, et tout ce qui ouvre des URL ou des scripts d'autres applications via AppleScript échouent avec l'erreur AppleScript -600 (« Application isn't running ») ou des erreurs LaunchServices (-10822, -54). Avertissement de sécurité : activer cette option signifie que le sandbox ne fournit plus d'isolation d'exécution de code. Une commande sandboxée peut lancer d'autres applications via open sans invite utilisateur, et tout ce qu'elle lance s'exécute en dehors des restrictions de système de fichiers et de réseau du sandbox ; le scripting d'applications déjà en cours d'exécution via Apple Events est en outre soumis au consentement d'automatisation TCC par application de l'utilisateur. Les intégrateurs ne doivent sourcer cette option qu'à partir d'une configuration utilisateur de confiance — jamais à partir de fichiers locaux au projet dans un dépôt extrait, ce qui permettrait à un projet rédigé par un attaquant d'élever ses propres permissions de sandbox.

Recettes de configuration courantes

Autoriser l'accès à GitHub (tous les points de terminaison nécessaires) :```json { "network": { "allowedDomains": [ "github.com", "*.github.com", "lfs.github.com", "api.github.com" ], "deniedDomains": [] }, "filesystem": { "denyRead": [], "allowWrite": ["."], "denyWrite": [] } }

**Restreindre à des répertoires spécifiques :**```json
{
  "network": {
    "allowedDomains": [],
    "deniedDomains": []
  },
  "filesystem": {
    "denyRead": ["~/.ssh"],
    "allowWrite": [".", "src/", "test/"],
    "denyWrite": [".env", "secrets/"]
  }
}

Accès au système de fichiers limité à l'espace de travail (refuser les lectures en dehors de l'espace de travail) :```json { "network": { "allowedDomains": [], "deniedDomains": [] }, "filesystem": { "denyRead": ["/Users"], "allowRead": ["."], "allowWrite": ["."], "denyWrite": [] } }

Cela interdit la lecture de tout ce qui se trouve sous `/Users` (ou `/home` sous Linux), puis réautorise le répertoire de travail actuel. Les chemins système (`/usr`, `/lib`, etc.) restent lisibles.

### Problèmes courants et astuces

**Exécuter Jest :** Utilisez l'option `--no-watchman` pour éviter les violations du bac à sable :```bash
srt "jest --no-watchman"

Watchman accède à des fichiers en dehors des limites du bac à sable, ce qui déclenchera des erreurs de permission. Le désactiver permet à Jest de fonctionner avec le surveillant de fichiers intégré à la place.

Prise en charge des plateformes

  • macOS : Utilise sandbox-exec avec des profils personnalisés (aucune dépendance supplémentaire)
  • Linux : Utilise bubblewrap (bwrap) pour la conteneurisation
  • Windows : Alpha — utilise un assistant srt-win.exe intégré (aucune dépendance supplémentaire). Voir Windows (alpha) ci-dessous pour la configuration, le modèle de sécurité et les limitations connues

Dépendances spécifiques à la plateforme

Linux nécessite :

  • bubblewrap - Runtime de conteneur
    • Ubuntu/Debian : apt-get install bubblewrap
    • Fedora : dnf install bubblewrap
    • Arch : pacman -S bubblewrap
  • socat - Relais de socket pour le pontage de proxy
    • Ubuntu/Debian : apt-get install socat
    • Fedora : dnf install socat
    • Arch : pacman -S socat
  • ripgrep - Outil de recherche rapide pour la détection des chemins refusés
    • Ubuntu/Debian : apt-get install ripgrep
    • Fedora : dnf install ripgrep
    • Arch : pacman -S ripgrep

Note pour Ubuntu 24.04+ : Ces versions activent kernel.apparmor_restrict_unprivileged_userns par défaut, ce qui autorise unshare(CLONE_NEWUSER) mais retire les capacités de l'espace de noms résultant. Bubblewrap et la couche d'isolation seccomp ont tous deux besoin d'espaces de noms utilisateur porteurs de capacités. Désactivez la restriction avec :```bash sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

ou ajoutez un profil AppArmor qui accorde `userns` aux binaires concernés.

**Dépendances Linux optionnelles (pour le repli seccomp) :**

Le paquet inclut des filtres BPF seccomp pré-générés pour les architectures x86-64 et arm. Ces dépendances ne sont nécessaires que si vous êtes sur une architecture différente où les filtres pré-générés ne sont pas disponibles :

- `gcc` ou `clang` - compilateur C
- `libseccomp-dev` - fichiers de développement de la bibliothèque Seccomp
  - Ubuntu/Debian : `apt-get install gcc libseccomp-dev`
  - Fedora : `dnf install gcc libseccomp-devel`
  - Arch : `pacman -S gcc libseccomp`

**macOS nécessite :**

- `ripgrep` - outil de recherche rapide pour la détection des chemins refusés
  - Installation via Homebrew : `brew install ripgrep`
  - Ou téléchargement depuis : https://github.com/BurntSushi/ripgrep/releases

**Windows nécessite :**

- Aucune dépendance supplémentaire. L'utilitaire `srt-win.exe` (x64 et arm64) est fourni avec le paquet npm. Une étape unique `windows-install` avec élévation de privilèges est requise — voir ci-dessous.

## Windows (alpha)

La prise en charge de Windows est en **alpha**. Le processus isolé s'exécute sous un compte utilisateur local dédié `srt-sandbox`, isolé de l'utilisateur appelant par des primitives de sécurité natives de Windows — une barrière de sortie Windows Filtering Platform (WFP) basée sur le SID du compte sandbox, et des ACE explicites par session qui accordent ou refusent à ce SID l'accès aux chemins du système de fichiers configurés.

### Configuration

À exécuter une fois par machine (auto-élévation ; une invite UAC) :```powershell
npx @anthropic-ai/sandbox-runtime windows-install

Cela provisionne le compte utilisateur local srt-sandbox (avec un mot de passe aléatoire stocké chiffré via DPAPI dans HKLM\SOFTWARE\sandbox-runtime — à l'échelle de la machine, de sorte que les installations de flotte s'exécutant en tant que SYSTEM fonctionnent et que la rotation d'un utilisateur met à jour la copie que les autres lisent), le groupe local sandbox-runtime-users, et installe un ensemble de filtres WFP à l'échelle de la machine indexé sur le SID srt-sandbox. Il est idempotent — le réexécuter fait tourner le mot de passe du compte sandbox et réconcilie l'ensemble de filtres.

Aucune déconnexion n'est requise. Les filtres WFP sont indexés sur le SID du compte sandbox dédié, de sorte que votre propre réseau, vos services et tout autre principal sur la machine ne sont pas affectés.

Après l'installation, SandboxManager.initialize() et la CLI srt fonctionnent comme sur les autres plateformes. initialize() vérifie que le compte sandbox et la clôture WFP sont actifs, et échoue avec une erreur exploitable dans le cas contraire.

L'installation/désinstallation programmatique est exposée via installWindowsSandbox() / uninstallWindowsSandbox().

Modèle de sécurité

La commande sandboxée s'exécute en tant que compte srt-sandbox, et non en tant qu'utilisateur appelant. L'utilitaire fourni srt-win.exe effectue un lancement en deux étapes : le broker appelle CreateProcessWithLogonW pour démarrer un runner en tant que srt-sandbox, et le runner lance la cible sous un jeton restreint à l'intérieur d'un objet job. L'enfant hérite du profil isolé du compte sandbox (%USERPROFILE%, %TEMP%, HKCU) et d'un environnement neuf superposé uniquement avec le PATH du broker et les variables de proxy générées.

L'exécution sous un SID utilisateur distinct ferme structurellement la classe d'évasion par spawn-surrogate (Task Scheduler, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS sur un processus appartenant au broker, BITS, COM hors processus avec RunAs="Interactive User") : tout processus que l'enfant parvient à lancer hors bande porte toujours le SID srt-sandbox, il reste donc soumis à la clôture d'égression WFP et n'a aucun droit sur les fichiers de l'utilisateur appelant.

L'isolation réseau est un ensemble de deux filtres WFP au niveau FWPM_LAYER_ALE_AUTH_CONNECT_V4/V6 : un PERMIT pour les destinations loopback à l'intérieur de la plage de ports proxy configurée (par défaut 60080–60089), et un BLOCK pour toute connexion dont le jeton porte le SID srt-sandbox. Le processus sandboxé n'atteint Internet que via les proxys HTTP/SOCKS5 JS écoutant dans cette plage ; un processus qui supprime son environnement proxy et se connecte directement est bloqué au niveau du noyau.

L'isolation du système de fichiers est assurée par les ACL discrétionnaires NTFS. Le compte srt-sandbox n'a aucun droit inhérent sur les fichiers de l'utilisateur appelant, donc à initialize() le sandbox écrit des ACE explicites additives et héritées pour le seul SID srt-sandbox — il ne réécrit ni ne remplace jamais le descripteur de sécurité existant d'un chemin :

  • filesystem.allowWrite → une ACE ALLOW MODIFY héritée (READ|WRITE|EXECUTE|DELETE, avec FILE_DELETE_CHILD retenu). Le processus sandboxé peut créer, modifier et supprimer des fichiers à l'intérieur de l'arborescence de travail ; retenir FILE_DELETE_CHILD de l'octroi est une défense en profondeur pour les tampons de refus ci-dessous, et non une protection sur la racine de l'arborescence.
  • filesystem.allowRead → une ACE ALLOW READ|EXECUTE héritée
  • filesystem.denyRead / filesystem.denyWrite → une ACE DENY héritée sur la cible, plus une ACE DENY FILE_DELETE_CHILD héritée sur son parent — conjointement avec le FILE_DELETE_CHILD retenu sur l'octroi de l'arborescence de travail, cela empêche le processus sandboxé de renommer ou supprimer un chemin refusé via son répertoire parent

reset() supprime chaque ACE ajoutée par cette session (comptée par référence entre les hôtes concurrents de cet utilisateur via la base de données de session par utilisateur ; une passe de récupération après incident au prochain initialize() nettoie après une sortie anormale). Les cibles de type répertoire sont prises en charge (les ACE héritent sur toute la sous-arborescence). Les motifs glob sont développés en chemins concrets au moment de initialize() — un chemin correspondant qui apparaît plus tard n'est pas couvert.

Terminaison TLS sous Windows

network.tlsTerminate exige que l'autorité de certification MITM soit présente dans le magasin de certificats CurrentUser\Root de l'utilisateur sandbox (schannel — le backend TLS utilisé par System32\curl.exe, PowerShell Invoke-WebRequest, .NET et git avec le backend par défaut — ne fait confiance qu'au magasin de l'OS, pas aux variables d'environnement). Il s'agit d'une étape au moment de l'installation, distincte de windows-install :```typescript import { windowsTrustCa } from '@anthropic-ai/sandbox-runtime' windowsTrustCa('/path/to/mitm-ca.crt') // or: srt-win user trust-ca

`initialize()` compare l'empreinte du CA de session avec celle installée et échoue avec un message exploitable en cas de non-concordance, afin qu'un CA obsolète installé au moment de l'installation ne puisse pas casser silencieusement TLS à l'intérieur du bac à sable.

Les clients basés sur OpenSSL (`curl` de msys2, `git -c http.sslBackend=openssl`, Node, Python, cargo) sont couverts par la couche de confiance basée sur les variables d'environnement : le même bundle de confiance utilisé sur macOS/Linux est transmis au bac à sable via `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO`, `CARGO_HTTP_CAINFO`, etc., et le chemin du bundle est ajouté à l'autorisation `allowRead` de la session afin que le compte du bac à sable puisse l'ouvrir.

### Configuration spécifique à Windows

Les blocs multiplateformes `filesystem` et `network` s'appliquent comme décrit ci-dessus. Les paramètres propres à Windows se trouvent sous `windows` :

- `windows.proxyPortRange` — plage de ports inclusive `[low, high]` dans laquelle les proxys JS se lient. **Doit correspondre** à la plage passée à `windows-install --proxy-port-range` (par défaut `[60080, 60089]`) — le PERMIT de bouclage WFP ne couvre que cette plage.
- `windows.sublayerGuid` — GUID de sous-couche WFP sous lequel les filtres ont été installés. À omettre pour utiliser la valeur par défaut à la compilation ; à définir uniquement lorsque des outils d'entreprise ont installé les filtres sous une sous-couche personnalisée.
- `windows.srtWin.path` — chemin vers le binaire `srt-win`. À omettre pour résoudre le `vendor/srt-win/<arch>/srt-win.exe` empaqueté. À définir lors de l'intégration de la CLI de `srt-win` dans un binaire multicall ; les spawns passent alors `--srt-win` comme `argv[1]` afin que le répartiteur de l'intégrateur puisse router vers `srt_win::run_from_args`.

### Limitations connues

- **Révocation de certificat sous schannel.** La récupération CRL/OCSP de CryptoAPI sort via WinHTTP sous le jeton de l'appelant, en ignorant l'environnement proxy, elle est donc bloquée par la barrière de sortie WFP. Les outils qui utilisent schannel avec la vérification de révocation activée par défaut échouent avec `CRYPT_E_REVOCATION_OFFLINE` (`0x80092013`) à moins que la révocation ne soit désactivée par outil : `curl --ssl-no-revoke`, `git -c http.schannelCheckRevoke=false`, `CARGO_HTTP_CHECK_REVOKE=false`. `Invoke-WebRequest`, `HttpClient` de .NET et `gh` ne vérifient pas la révocation par défaut et ne sont pas affectés. Un point de distribution CRL servi depuis le proxy de bouclage est prévu pour supprimer ce contournement.
- **Les installations d'outils par utilisateur ne sont pas accessibles.** Le processus en bac à sable s'exécute en tant que `srt-sandbox`, pas en tant que vous, donc les outils installés sous votre profil (Node géré par nvm/fnm, paquets `winget`/Scoop par utilisateur, `pip install --user`, `%LOCALAPPDATA%\Programs\…`) se résolvent sur le `PATH` hérité mais ne peuvent pas être ouverts par le compte du bac à sable. Préférez les installations à l'échelle de la machine (`Program Files`, `choco`/`winget --scope machine`), ou ajoutez les chemins de profil spécifiques à `filesystem.allowRead`.
- **Les surcharges `filesystem.allowRead` / `filesystem.allowWrite` par exécution ne sont pas prises en charge.** Les `allowRead`/`allowWrite` au niveau de la session (dans la configuration passée à `initialize()`) fonctionnent comme décrit ci-dessus ; les passer par commande dans le `customConfig` de `wrapWithSandbox` lève une exception — les autorisations sont appliquées à l'échelle de la session via `srt-win acl grant` à `initialize()`, et `srt-win exec` n'expose que les refus par exécution.
- **`proxyAuthToken` est visible dans la ligne de commande du runner.** L'environnement proxy (y compris `HTTP_PROXY=http://srt:<token>@127.0.0.1:…`) est passé au runner à deux sauts sous forme d'arguments `--env` dans l'argv de `srt-win exec`, donc le jeton est lisible par tout principal local capable d'ouvrir le processus du runner pour `PROCESS_QUERY_LIMITED_INFORMATION`. Le jeton existe pour que le processus en bac à sable puisse s'authentifier auprès du proxy de bouclage, il n'est donc pas un secret vis-à-vis du bac à sable lui-même ; sur une machine de développement mono-utilisateur, cela est généralement acceptable, mais sur un hôte partagé, considérez la liste d'autorisation du proxy comme accessible par d'autres principaux de la même session.
- **La résolution DNS via le résolveur système n'est pas filtrée.** `getaddrinfo()` est pris en charge par le service `Dnscache` s'exécutant en tant que `NETWORK SERVICE`, donc la résolution de noms réussit même si le `connect()` ultérieur depuis le processus en bac à sable est bloqué. Les outils qui font leur propre UDP/53 (`nslookup`, `dig`) sont filtrés. Cela reflète le comportement de macOS.

### Désinstallation```powershell
npx @anthropic-ai/sandbox-runtime windows-uninstall

Supprime le jeu de filtres WFP, le compte srt-sandbox et son profil, le groupe sandbox-runtime-users, et supprime la clé HKLM\SOFTWARE\sandbox-runtime (identifiant, marqueur, enregistrement CA) — une invite UAC. %ProgramData%\sandbox-runtime (le matériel de clé CA) est laissé en place ; supprimez-le (ainsi que %LOCALAPPDATA%\sandbox-runtime par utilisateur) manuellement pour un nettoyage complet.

Développement```bash

Install dependencies

npm install

Build the project

npm run build

Run tests

npm test

Type checking

npm run typecheck

Lint code

npm run lint

Format code

npm run format

### Compilation des binaires Seccomp

Le filtre BPF et le chargeur `apply-seccomp` sont compilés à partir du code source C dans `vendor/seccomp-src/` via `npm run build:seccomp` (Linux uniquement ; nécessite `gcc` et `libseccomp-dev`). La CI l'exécute avant les tests sur chaque architecture Linux, et le workflow de release compile les deux architectures et les intègre au paquet publié.

## Détails d'implémentation

### Architecture d'isolation réseau

Le sandbox exécute des serveurs proxy HTTP et SOCKS5 sur la machine hôte qui filtrent toutes les requêtes réseau en fonction des règles de permission :

1. **Trafic HTTP/HTTPS** : Un serveur proxy HTTP intercepte les requêtes et les valide par rapport aux domaines autorisés/interdits
2. **Autre trafic réseau** : Un proxy SOCKS5 gère toutes les autres connexions TCP (SSH, connexions aux bases de données, etc.)
3. **Application des permissions** : Les proxys appliquent les règles `permissions` de votre configuration

**Communication proxy spécifique à la plateforme :**

- **Linux** : Les requêtes sont acheminées via le système de fichiers par des sockets de domaine Unix (en utilisant `socat` pour le pontage). L'espace de noms réseau est retiré du conteneur bubblewrap, garantissant que tout le trafic réseau doit passer par les proxys.

- **macOS** : Le profil Seatbelt autorise la communication uniquement vers des ports localhost spécifiques où les proxys écoutent. Tout autre accès réseau est bloqué.

- **Windows** : Un filtre WFP `ALE_AUTH_CONNECT` bloque toute connexion sortante depuis le compte `srt-sandbox` sauf le loopback vers la plage de ports proxy configurée. Les proxys se lient à l'intérieur de cette plage. Les variables d'environnement (`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`, …) pointent les outils vers les proxys, mais le filtre WFP constitue la frontière — un processus qui les ignore ou les désactive est toujours cloisonné.

**Outils JVM (macOS/Linux) :** la JVM ignore `HTTPS_PROXY`/`NO_PROXY` et n'a pas de variable d'environnement pour les identifiants de proxy — la sélection du proxy provient des propriétés système `https.proxyHost` et l'identifiant ne peut être fourni que via `java.net.Authenticator`. Ainsi, les outils basés sur la JVM (cache distant gRPC de Bazel, Gradle, Maven, …) composeraient sinon directement la cible et échoueraient, ou atteindraient le proxy sans son jeton et recevraient un 407. Pour combler cette lacune, srt injecte un petit `-javaagent` via `JAVA_TOOL_OPTIONS` (la variable d'environnement ne contient que le chemin du jar, l'identifiant reste dans `HTTPS_PROXY`). Au démarrage de la JVM, l'agent définit `http[s].proxyHost`/`Port` et `http.nonProxyHosts` à partir des variables d'environnement du proxy, réactive l'authentification Basic pour les tunnels CONNECT, et installe un Authenticator pour le point de terminaison du proxy. Les propriétés de proxy `-D` explicites sur la ligne de commande de la JVM l'emportent toujours, et tout `JAVA_TOOL_OPTIONS` hérité est préservé (sauf s'il s'agit d'une variable d'environnement d'identifiant refusée). Chaque JVM affiche une ligne `Picked up JAVA_TOOL_OPTIONS: …` sur stderr en conséquence ; un runtime jlink'd construit sans le module `java.instrument` ne peut pas charger d'agents et refusera de démarrer sous le sandbox — désactivez `JAVA_TOOL_OPTIONS` dans la commande pour un tel outil. Le jar est livré dans le paquet npm sous `vendor/java-proxy-agent/srt-proxy-agent.jar` (source : `vendor/java-proxy-agent-src/` ; construit par le workflow de release, ou localement avec `npm run build:java-agent` — nécessite un JDK ≥ 17). S'il n'est pas trouvé, `JAVA_TOOL_OPTIONS` est laissé tel quel et les JVM se comportent comme avant ; les bundlers peuvent pointer vers leur propre copie avec `javaAgentJarPath`.

### Isolation du système de fichiers

Les restrictions du système de fichiers sont appliquées au niveau de l'OS :

- **macOS** : Utilise `sandbox-exec` avec des profils Seatbelt générés dynamiquement qui spécifient les chemins de lecture/écriture autorisés
- **Linux** : Utilise `bubblewrap` avec des montages bind, marquant les répertoires en lecture seule ou lecture-écriture selon la configuration
- **Windows** : Écrit des ACEs explicites additifs `(OI)(CI)` pour le SID `srt-sandbox` sur les chemins configurés (ALLOW sur `allowRead`/`allowWrite`, DENY sur `denyRead`/`denyWrite`), puis les supprime lors de `reset()`

**Permissions par défaut du système de fichiers :**

- **Lecture** (refuser-puis-autoriser) : Autorisée partout par défaut. Vous pouvez refuser de vastes régions, puis réautoriser des chemins spécifiques à l'intérieur. `allowRead` a la priorité sur `denyRead`.

  - Exemple : `denyRead: ["~/.ssh"]` pour bloquer l'accès aux clés SSH
  - Exemple : `denyRead: ["/Users"], allowRead: ["."]` pour bloquer tout `/Users` sauf l'espace de travail
  - `denyRead: []` vide = accès complet en lecture (rien de refusé)

- **Écriture** (autoriser uniquement) : Refusée partout par défaut. Vous devez explicitement autoriser des chemins.
  - Exemple : `allowWrite: [".", "/tmp"]` pour autoriser les écritures dans le répertoire courant et /tmp
  - `allowWrite: []` vide = aucun accès en écriture (rien d'autorisé)
  - `denyWrite` crée des exceptions dans les chemins autorisés (le refus a la priorité)

**La priorité est intentionnellement opposée pour les lectures vs les écritures :** `allowRead` remplace `denyRead`, tandis que `denyWrite` remplace `allowWrite`. Cela vous permet de délimiter des régions lisibles dans des zones refusées, et de délimiter des régions protégées dans des zones inscriptibles.

### Chemins de refus obligatoires (fichiers auto-protégés)

Certains fichiers et répertoires sensibles sont **toujours bloqués en écriture**, même s'ils se trouvent dans un chemin d'écriture autorisé. Cela fournit une défense en profondeur contre les évasions de sandbox et la falsification de configuration.

**Fichiers toujours bloqués :**

- Fichiers de configuration du shell : `.bashrc`, `.bash_profile`, `.zshrc`, `.zprofile`, `.profile`
- Fichiers de configuration Git : `.gitconfig`, `.gitmodules`
- Autres fichiers sensibles : `.ripgreprc`, `.mcp.json`

**Répertoires toujours bloqués :**

- Répertoires IDE : `.vscode/`, `.idea/`
- Répertoires de configuration Claude : `.claude/commands/`, `.claude/agents/`
- Hooks et configuration Git : `.git/hooks/`, `.git/config`

Ces chemins sont bloqués automatiquement - vous n'avez pas besoin de les ajouter à `denyWrite`. Par exemple, même avec `allowWrite: ["."]`, écrire dans `.bashrc` ou `.git/hooks/pre-commit` échouera :```bash
$ srt 'echo "malicious" >> .bashrc'
/bin/bash: .bashrc: Operation not permitted

$ srt 'echo "bad" > .git/hooks/pre-commit'
/bin/bash: .git/hooks/pre-commit: Operation not permitted

Remarque (Linux) : Sous Linux, les chemins de refus obligatoires ne bloquent que les fichiers qui existent déjà. Les fichiers inexistants correspondant à ces motifs ne peuvent pas être bloqués par l'approche de montage par liaison de bubblewrap. macOS utilise des motifs glob qui bloquent à la fois les fichiers existants et nouveaux.

Profondeur de recherche sous Linux : Sous Linux, le bac à sable utilise ripgrep pour rechercher les fichiers dangereux dans les sous-répertoires situés dans les chemins d'écriture autorisés. Par défaut, il recherche jusqu'à 3 niveaux de profondeur pour des raisons de performance. Vous pouvez configurer cela avec mandatoryDenySearchDepth :```json { "mandatoryDenySearchDepth": 5, "filesystem": { "allowWrite": ["."] } }

- Par défaut : `3` (recherche jusqu'à 3 niveaux de profondeur)
- Plage : `1` à `10`
- Des valeurs plus élevées offrent une meilleure protection mais des performances plus lentes
- Les fichiers dans le CWD (profondeur 0) sont toujours protégés, quel que soit ce paramètre

### Restrictions des sockets Unix (Linux)

Sur Linux, le bac à sable utilise **seccomp BPF (Berkeley Packet Filter)** pour bloquer la création de sockets de domaine Unix au niveau de l'appel système. Cela fournit une couche de sécurité supplémentaire pour empêcher les processus de créer de nouvelles sockets de domaine Unix pour l'IPC local (sauf autorisation explicite).

**Fonctionnement :**

1. **Filtre BPF intégré** : Le paquet fournit un binaire statique `apply-seccomp` pour x64 et arm64 avec le filtre seccomp BPF compilé à l'intérieur. Le filtre est spécifique à l'architecture mais indépendant de la libc, de sorte que le binaire fonctionne à la fois avec glibc et musl.

2. **Détection à l'exécution** : Le bac à sable détecte automatiquement l'architecture de votre système et utilise le binaire `apply-seccomp` correspondant.

3. **Filtrage des appels système** : Le filtre BPF intercepte l'appel système `socket()` et bloque la création de sockets `AF_UNIX` en renvoyant `EPERM`. Cela empêche le code en bac à sable de créer de nouvelles sockets de domaine Unix.

4. **Application en deux étapes à l'aide du binaire apply-seccomp** :
   - Le bwrap externe crée le bac à sable avec des restrictions de système de fichiers, de réseau et d'espace de noms PID
   - Les processus de pontage réseau (socat) démarrent à l'intérieur du bac à sable (nécessitent des sockets Unix)
   - apply-seccomp crée un espace de noms imbriqué user+PID+mount et remonte `/proc`
   - À l'intérieur de l'espace de noms imbriqué, apply-seccomp agit comme PID 1 (init/reaper non dumpable)
   - apply-seccomp fork, applique le filtre seccomp via `prctl()`, et exécute la commande utilisateur
   - La commande utilisateur s'exécute avec toutes les restrictions du bac à sable plus le blocage de la création de sockets Unix

**Isolation de l'espace de noms PID** : L'espace de noms PID imbriqué garantit que la commande utilisateur ne peut ni voir ni adresser aucun processus qui s'exécute sans le filtre seccomp (l'init de bwrap, le wrapper shell, ou les assistants socat). Cela maintient la frontière seccomp intacte quel que soit `kernel.yama.ptrace_scope`, puisque les assistants non filtrés ne sont pas accessibles via `ptrace` ou `/proc/N/mem`. Le PID 1 interne définit `PR_SET_DUMPABLE=0` afin qu'il ne soit pas non plus ptraceable. Si la création de l'espace de noms imbriqué échoue, apply-seccomp abandonne plutôt que de s'exécuter sans isolation.

**Limitations de sécurité** : Le filtre bloque `socket(AF_UNIX, ...)` et les appels système `io_uring_setup`/`io_uring_enter`/`io_uring_register` (les trois derniers car `IORING_OP_SOCKET` sur Linux 5.19+ contournerait autrement la règle `socket()`). Il n'empêche pas les opérations sur les descripteurs de fichiers de sockets Unix hérités des processus parents ou transmis via `SCM_RIGHTS`. Pour la plupart des scénarios de bac à sable, bloquer la création de sockets est suffisant pour empêcher l'IPC non autorisé.

**Aucune dépendance à l'exécution** : Des binaires apply-seccomp statiques précompilés et des filtres BPF pré-générés sont inclus pour les architectures x64 et arm64. Aucun outil de compilation ni dépendance externe requis à l'exécution.

**Prise en charge des architectures** : x64 et arm64 sont entièrement pris en charge avec des binaires précompilés. Les autres architectures ne sont pas prises en charge actuellement. Pour utiliser le bac à sable sans blocage des sockets Unix sur des architectures non prises en charge, définissez `allowAllUnixSockets: true` dans votre configuration.

### Détection et surveillance des violations

Lorsqu'un processus en bac à sable tente d'accéder à une ressource restreinte :

1. **Bloque l'opération** au niveau du système d'exploitation (renvoie une erreur `EPERM`)
2. **Journalise la violation** (mécanismes spécifiques à la plateforme)
3. **Notifie l'utilisateur** (dans Claude Code, cela déclenche une invite d'autorisation)

**macOS** : Le runtime du bac à sable exploite le magasin de journaux de violations du bac à sable système de macOS. Cela fournit des notifications en temps réel avec des informations détaillées sur ce qui a été tenté et pourquoi cela a été bloqué. C'est le même mécanisme que Claude Code utilise pour la détection des violations.```bash
# View sandbox violations in real-time
log stream --predicate 'process == "sandbox-exec"' --style syslog

Linux : Bubblewrap ne fournit pas de rapport de violation intégré. Utilisez strace pour tracer les appels système et identifier les opérations bloquées :```bash

Trace all denied operations

strace -f srt 2>&1 | grep EPERM

Trace specific file operations

strace -f -e trace=open,openat,stat,access srt 2>&1 | grep EPERM

Trace network operations

strace -f -e trace=network srt 2>&1 | grep EPERM

### Avancé : Apportez votre propre proxy

Pour un filtrage réseau plus sophistiqué, vous pouvez configurer le sandbox pour utiliser votre propre proxy au lieu de ceux intégrés. Cela permet :

- **Inspection du trafic** : Utilisez des outils comme [mitmproxy](https://mitmproxy.org/) pour inspecter et modifier le trafic
- **Logique de filtrage personnalisée** : Implémentez des règles complexes au-delà des simples listes d'autorisation de domaines
- **Journalisation d'audit** : Enregistrez toutes les requêtes réseau pour la conformité ou le débogage

**Exemple avec mitmproxy :**```bash
# Start mitmproxy with custom filtering script
mitmproxy -s custom_filter.py --listen-port 8888

Remarque : La configuration d'un proxy personnalisé n'est pas encore prise en charge dans le nouveau format de configuration. Cette fonctionnalité sera ajoutée dans une version future.

Considération de sécurité importante : Même avec des listes d'autorisation de domaines, des vecteurs d'exfiltration peuvent exister. Par exemple, autoriser github.com permet à un processus de pousser vers n'importe quel dépôt. Avec un proxy MITM personnalisé et une configuration de certificat appropriée, vous pouvez inspecter et filtrer des appels API spécifiques pour éviter cela.

Limitations de sécurité

  • Limitations du sandboxing réseau : Le système de filtrage réseau fonctionne en restreignant les domaines auxquels les processus sont autorisés à se connecter. Il n'inspecte pas autrement le trafic passant par le proxy et les utilisateurs sont responsables de s'assurer qu'ils n'autorisent que des domaines de confiance dans leur politique. Les noms d'hôtes autorisés sont en outre vérifiés par rapport à un ensemble refusé d'adresses résolues avant une connexion directe (voir Vérification des adresses résolues ci-dessus), de sorte qu'un nom autorisé ne peut pas être pointé vers loopback, link-local, les adresses propres de cet hôte ou une IP que vous avez listée dans deniedDomains ; les autres plages privées ne sont couvertes que si vous les listez dans deniedResolvedAddresses (une entrée générique sur un domaine dont vous ne contrôlez pas le DNS peut autrement être dirigée vers des services sur votre LAN), et les connexions qui sortent via parentProxy/mitmProxy dépendent de ce saut pour la vérification équivalente.
Les utilisateurs doivent être conscients des risques potentiels liés à l'autorisation de domaines larges comme `github.com` qui peuvent permettre l'exfiltration de données. De plus, dans certains cas, il peut être possible de contourner le filtrage réseau via le [domain fronting](https://en.wikipedia.org/wiki/Domain_fronting).
  • Élévation de privilèges via les sockets Unix : La configuration allowUnixSockets peut accidentellement accorder l'accès à des services système puissants pouvant mener à des contournements du sandbox. Par exemple, si elle est utilisée pour autoriser l'accès à /var/run/docker.sock, cela accorderait effectivement l'accès au système hôte en exploitant le socket docker. Les utilisateurs sont encouragés à examiner attentivement tout socket unix qu'ils autorisent à travers le sandbox.
  • Élévation des permissions du système de fichiers : Des permissions d'écriture trop larges sur le système de fichiers peuvent permettre des attaques d'élévation de privilèges. Autoriser les écritures dans des répertoires contenant des exécutables dans $PATH, des répertoires de configuration système, ou des fichiers de configuration de shell utilisateur (.bashrc, .zshrc) peut mener à l'exécution de code dans différents contextes de sécurité lorsque d'autres utilisateurs ou processus système accèdent à ces fichiers.
  • Robustesse du sandbox Linux : L'implémentation Linux fournit un isolement fort du système de fichiers et du réseau mais inclut un mode enableWeakerNestedSandbox qui lui permet de fonctionner à l'intérieur d'environnements Docker sans namespaces privilégiés. Cette option affaiblit considérablement la sécurité et ne doit être utilisée que dans les cas où un isolement supplémentaire est autrement appliqué.
  • Isolement réseau plus faible (macOS) : L'option enableWeakerNetworkIsolation réactive l'accès à com.apple.trustd.agent, nécessaire aux programmes Go pour vérifier les certificats TLS via le framework Security de macOS. Cela ouvre un vecteur potentiel d'exfiltration de données via le service trustd et ne doit être activé que lorsque la vérification TLS de Go est requise (par exemple, lors de l'utilisation de httpProxyPort avec un proxy MITM et une CA personnalisée).
  • Apple Events (macOS) : L'option allowAppleEvents réactive l'envoi d'Apple Events et les requêtes d'ouverture Launch Services ((allow appleevent-send), (allow lsopen), et les mach-lookups pour com.apple.coreservices.appleevents, com.apple.CoreServices.coreservicesd, et com.apple.coreservices.quarantine-resolver), dont open, osascript et les assistants d'ouverture d'URL ont besoin. Avec ces autorisations, une commande sandboxée peut lancer des applications arbitraires sans invite utilisateur, et les applications lancées s'exécutent entièrement en dehors du sandbox — donc cette option supprime l'isolement d'exécution de code, pas seulement l'affaiblit. Le scripting d'applications déjà en cours d'exécution via Apple Events est en outre soumis au consentement d'automatisation TCC de macOS, mais le lancement via open ne l'est pas. N'activez ceci que lorsque les commandes à l'intérieur du sandbox ont réellement besoin d'ouvrir des URL ou des applications.

Limitations connues et travaux futurs

Contournement du proxy Linux : Utilise actuellement des variables d'environnement (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY) pour diriger le trafic à travers les proxies. Cela fonctionne pour la plupart des applications mais peut être ignoré par des programmes qui ne respectent pas ces variables, les empêchant de se connecter à Internet.

Améliorations futures :

  • Prise en charge de Proxychains : Ajouter la prise en charge de proxychains avec LD_PRELOAD sur Linux pour intercepter les appels réseau à un niveau inférieur, rendant le contournement plus difficile

  • Surveillance des violations sous Linux : Implémenter une détection automatique des violations basée sur strace pour Linux, intégrée au registre des violations. Actuellement, les utilisateurs Linux doivent exécuter manuellement strace pour voir les violations, contrairement à macOS qui dispose d'une surveillance automatique des violations via le registre des journaux système

Catégories