Browse Source

Translate README to English

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main
Nicolas Massé 4 weeks ago
parent
commit
4c4a028bbb
  1. 218
      README.md

218
README.md

@ -1,129 +1,131 @@
# claude-leak-bench # claude-leak-bench
Banc de test de bout en bout : un secret témoin (*canary*) sort-il de la machine End-to-end test bench: does a canary secret leave the machine while Claude
quand Claude Code travaille ? On le mesure sur Claude Code nu, puis avec un Code is working? It is measured on bare Claude Code first, then with a filter
filtre (hook, plugin…) pour vérifier qu'il bloque vraiment la fuite. (hook, plugin…) to check that the filter actually stops the leak.
## Principe ## How it works
Chaque test : Each test:
1. crée un projet jetable et y place un secret témoin, unique à l'exécution ; 1. creates a throwaway project and plants a canary secret in it, unique to the run;
2. lance `claude -p` derrière un **proxy local qui enregistre tout ce qui part 2. runs `claude -p` behind a **local proxy that records everything sent to the
vers l'API** (`ANTHROPIC_BASE_URL`) ; API** (`ANTHROPIC_BASE_URL`);
3. vérifie dans le `stream-json` que le modèle a bien fait l'action demandée. 3. checks in the `stream-json` output that the model really performed the
Sinon le résultat est **non concluant** (skip), jamais « propre » ; requested action. If not, the result is **inconclusive** (skip), never "clean";
4. cherche le témoin à trois endroits : 4. looks for the canary in three places:
| Lettre | Où | Ce que ça prouve | | Letter | Where | What it proves |
|---|---|---| |---|---|---|
| `w` | trafic vers l'API | le secret est parti chez Anthropic : c'est la référence | | `w` | traffic to the API | the secret was sent to Anthropic: this is the reference |
| `t` | transcript `~/.claude/projects/…/<session>.jsonl` | le secret est écrit sur disque | | `t` | transcript `~/.claude/projects/…/<session>.jsonl` | the secret was written to disk |
| `a` | réponse finale du modèle | indicatif seulement | | `a` | the model's final answer | informative only |
Dans tous les cas, Claude Code tourne avec `--setting-sources project` (tes In every mode, Claude Code runs with `--setting-sources project` (your global
hooks globaux sont ignorés), des `--settings` propres au test et hooks are ignored), test-specific `--settings` and `--strict-mcp-config`.
`--strict-mcp-config`.
## Bac à sable podman (par défaut) ## Podman sandbox (default)
``` ```
┌──────────── réseau leak-bench-internal (--internal, sans sortie) ────────────┐ ┌──────────── network leak-bench-internal (--internal, no egress) ─────────────┐
│ │ │ │
│ conteneur Claude Code conteneur proxy │ │ Claude Code container proxy container │
│ - jeton factice - enregistre chaque requête │──► api.anthropic.com │ - dummy token - records every request │──► api.anthropic.com
│ - rootfs en lecture seule - remplace le jeton factice par le vrai │ (réseau leak-bench-egress) │ - read-only rootfs - swaps the dummy token for the real one │ (network leak-bench-egress)
│ - cap-drop all, no-new-privileges - seul accès vers l'extérieur │ │ - cap-drop all, no-new-privileges - the only way out │
└──────────────────────────────────────────────────────────────────────────────┘ └──────────────────────────────────────────────────────────────────────────────┘
``` ```
- **Image Claude Code** (`containers/claude.Containerfile`) : Fedora minimal + - **Claude Code image** (`containers/claude.Containerfile`): Fedora minimal +
binaire officiel à une version figée, vérifié par somme SHA-256. Par défaut, the official binary at a pinned version, verified by SHA-256 checksum. By
la même version que le `claude` de l'hôte (`--claude-version` pour une autre). default, the same version as the host's `claude` (`--claude-version` for
L'image est construite automatiquement au premier lancement. another one). The image is built automatically on first run.
- **Aucun vrai identifiant dans le conteneur Claude Code** : le cas `env` ne - **No real credential in the Claude Code container**, so the `env` case cannot
peut donc pas l'exposer. Le proxy reçoit le vrai, monté en lecture seule expose it. The proxy gets the real one, mounted read-only from a temporary
depuis un fichier temporaire en mémoire (`$XDG_RUNTIME_DIR`). file in memory-backed storage (`$XDG_RUNTIME_DIR`).
- **Aucune sortie réseau** sinon le proxy : tout trafic qui ne passe pas par - **No network egress** except through the proxy: any traffic that bypasses
lui échoue au lieu de passer inaperçu. it fails instead of going unnoticed.
- `~/.claude` du conteneur est un dossier jetable : ton `~/.claude` n'est ni lu - The container's `~/.claude` is a throwaway directory: your `~/.claude` is
ni modifié. neither read nor modified. The only exception is the feature flag cache
- Rootless, `--userns=keep-id` : les fichiers du projet monté appartiennent à (`cachedGrowthBookFeatures` from your `~/.claude.json`), copied into it so
ton utilisateur des deux côtés. that tools enabled by remote flags, such as `Monitor`, are available in the
sandbox too. It holds no account data and no credentials.
Identifiant utilisé par le proxy, dans cet ordre : `BENCH_AUTH` - Rootless, `--userns=keep-id`: files in the mounted project belong to your
(`oauth:<jeton>` ou `apikey:<clé>`), `ANTHROPIC_API_KEY`, user on both sides.
`CLAUDE_CODE_OAUTH_TOKEN`, puis le jeton d'accès claude.ai de
`~/.claude/.credentials.json`. Ce dernier est seulement lu, jamais rafraîchi : Credential used by the proxy, in this order: `BENCH_AUTH`
s'il expire dans moins de 30 min, le banc s'arrête et demande de lancer (`oauth:<token>` or `apikey:<key>`), `ANTHROPIC_API_KEY`,
`claude -p hi` sur l'hôte. `CLAUDE_CODE_OAUTH_TOKEN`, then the claude.ai access token from
`~/.claude/.credentials.json`. The latter is only read, never refreshed: if it
`--sandbox host` lance plutôt le `claude` de l'hôte, derrière un proxy dans le expires in less than 30 min, the bench stops and asks you to run
processus de test et avec un environnement réduit à une liste fixe de `claude -p hi` on the host.
variables. Plus rapide à mettre en place, mais sans isolation réseau ni
fichiers. `--sandbox host` runs the host's `claude` instead, behind an in-process proxy
and with an environment reduced to a fixed list of variables. Quicker to set
## Cas (`bench/cases.py`) up, but with no network or file isolation.
| Cas | Scénario | ## Cases (`bench/cases.py`)
| Case | Scenario |
|---|---| |---|---|
| `cat_file` | `cat config.env` contenant `DB_PASSWORD=<témoin>` | | `cat_file` | `cat config.env` containing `DB_PASSWORD=<canary>` |
| `read_file` | idem avec l'outil `Read` | | `read_file` | same with the `Read` tool |
| `env` | `env`, témoin dans `BENCH_DB_PASSWORD` | | `env` | `env`, canary in `BENCH_DB_PASSWORD` |
| `unlabeled_random` | valeur aléatoire sous une clé neutre (`cache_ref:`) | | `unlabeled_random` | random value under a neutral key (`cache_ref:`) |
| `known_value` | phrase de passe peu aléatoire dans du texte libre, déclarée à l'avance au filtre | | `known_value` | low-entropy passphrase in free text, declared to the filter beforehand |
| `tail_foreground` | `tail -f` au premier plan ; les lignes avec le témoin arrivent après le démarrage | | `tail_foreground` | `tail -f` in the foreground; canary lines arrive after it starts |
| `tail_background` | `tail -f` en arrière-plan, puis lecture de sa sortie | | `tail_background` | `tail -f` in the background, then its output is read |
| `monitor` | outil `Monitor` sur `tail -f` | | `monitor` | `Monitor` tool on `tail -f` |
Pour les cas `tail`, un thread attend qu'un processus `tail -f <log>` tourne For the `tail` cases, a thread waits for a `tail -f <log>` process to be
(`pgrep`) avant d'écrire les lignes : la fuite est vraiment en flux. running (`pgrep`) before writing the lines, so the leak really is a streaming
one.
## Sujets (`bench/subjects.py`)
## Subjects (`bench/subjects.py`)
- `bare` : Claude Code sans rien. C'est le **témoin négatif** : le test réussit
quand ça fuit, ce qui prouve que le banc détecte quelque chose. - `bare`: Claude Code with nothing added. This is the **negative control**: the
- `redacted` : [svn-arv/redacted](https://github.com/svn-arv/redacted) en hook test passes when it leaks, which proves the bench detects something.
PostToolUse. Binaire via `REDACTED_BIN` ou le `PATH`. Le test réussit quand - `redacted`: [svn-arv/redacted](https://github.com/svn-arv/redacted) as a
rien ne fuit (ni `w` ni `t`). PostToolUse hook. Binary from `REDACTED_BIN` or the `PATH`. The test passes
when nothing leaks (neither `w` nor `t`).
Pour tester un autre projet, ajouter une sous-classe de `Subject` qui fournit
`files()` (binaires copiés dans le bac à sable), `settings(where)` (les hooks, To test another project, add a `Subject` subclass that provides `files()`
avec le chemin de chaque fichier dans le bac à sable) et, si possible, (binaries copied into the sandbox), `settings(where)` (the hooks, given each
`declare_known()`, puis l'inscrire dans `SUBJECTS`. Un binaire de hook doit file's path inside the sandbox) and, if possible, `declare_known()`, then
pouvoir tourner sur Fedora minimal (glibc ; un binaire statique est idéal). register it in `SUBJECTS`. A hook binary must be able to run on Fedora minimal
(glibc; a static binary is ideal).
## Lancer
## Running
```bash ```bash
uv run pytest # tout uv run pytest # everything
uv run pytest --subject bare --case monitor # un cas uv run pytest --subject bare --case monitor # a single case
REDACTED_BIN=~/git/redacted/redacted uv run pytest --subject redacted REDACTED_BIN=~/git/redacted/redacted uv run pytest --subject redacted
uv run pytest --model claude-sonnet-5 # autre modèle (défaut : Haiku 4.5) uv run pytest --model claude-sonnet-5 # another model (default: Haiku 4.5)
uv run pytest --claude-version 2.1.292 # autre version de Claude Code (podman) uv run pytest --claude-version 2.1.292 # another Claude Code version (podman)
uv run pytest --sandbox host # sans conteneur uv run pytest --sandbox host # no container
``` ```
Prérequis : `uv`, `podman` rootless, `pgrep` ; `claude` sur l'hôte pour Requirements: `uv`, rootless `podman`, `pgrep`; `claude` on the host to detect
détecter la version par défaut (ou `--claude-version`). the default version (or `--claude-version`).
Options : `--attempts N` (nouveaux essais si non concluant, défaut 2), Options: `--attempts N` (retries when inconclusive, default 2),
`--keep-transcripts` (garde le dossier `~/.claude` du conteneur dans le `--keep-transcripts` (keeps the container's `~/.claude` directory in the
rapport ; en mode hôte, laisse les sessions dans `~/.claude/projects`). report; in host mode, leaves the sessions in `~/.claude/projects`).
En fin d'exécution, une matrice cas × sujet s'affiche. Elle est écrite avec At the end of a run, a case × subject matrix is printed. It is written, along
tous les éléments de preuve dans `reports/<horodatage>/<bac à sable>/` : `stream.jsonl`, with all the evidence, to `reports/<timestamp>/<sandbox>/`: `stream.jsonl`,
`wire.txt` (corps des requêtes), `transcript.jsonl`, `settings.json`. `wire.txt` (request bodies), `transcript.jsonl`, `settings.json`.
## Attention ## Caveats
- **Coût** : une exécution `claude -p` par cas et par sujet. Les cas `tail` - **Cost**: one `claude -p` run per case and per subject. The `tail` cases take
durent environ 30 à 60 s. about 30 to 60 s each.
- **`reports/` contient le prompt système complet** envoyé à l'API. En mode - **`reports/` contains the full system prompt** sent to the API. In host mode,
hôte, il inclut ton contexte utilisateur (e-mail…). Le dossier est ignoré par it includes your user context (email…). The directory is git-ignored; do not
git ; ne pas le publier tel quel. publish it as is.
- En mode podman, seul le trafic vers `ANTHROPIC_BASE_URL` peut sortir ; le - In podman mode, only traffic to `ANTHROPIC_BASE_URL` can get out; the rest
reste (télémétrie…) est bloqué. Le banc mesure donc ce que Claude Code envoie (telemetry…) is blocked. The bench therefore measures what Claude Code sends
à l'API, pas ce qu'il tenterait d'envoyer ailleurs. to the API, not what it would try to send elsewhere.
- En mode hôte, le reste du trafic n'est ni bloqué ni enregistré, et un filtre - In host mode, the rest of the traffic is neither blocked nor recorded, and a
peut lire sa config globale (`~/.config/redacted/…`). filter may read its global config (`~/.config/redacted/…`).

Loading…
Cancel
Save