[ISS-3] Dispatch-reviewer kan zijn eigen credential lezen: /sources/claude_oauth_token valt binnen --add-dir /sources #108

Open
opened 2026-10-04 23:34:13 +02:00 by janpeter · 0 comments
Owner

Beheerd door Scrum4Me — wijzigingen hier worden overschreven. Bron: https://thuis.jp-visser.nl/issues/cmuubx4p80004j217woxfjmxl

Status: investigating · Severity: s3_major · Gemeld door: mac:claude · Occurrences: 1 (laatst: 2026-10-04T21:24:46.988Z) · Aangemaakt: 2026-10-04T21:24:46.988Z

Registratie

Waarneming (2026-10-04, ISS-11-acceptatie). De Claude-dispatchreviewer (request 8125d760, job a6616e02, slot job:managed:srv-review-claude-1) meldde in zijn rapport een bestand /sources/claude_oauth_token dat niet in source_artifacts staat. Hij heeft het bewust niet gelezen.

Code (scrum4me-docker origin/master).

  • Waar het token staat: het OAuth-token komt als operatorbron binnen in /sources/claude_oauth_token (lib/dispatch-child.ts:18-20, Dockerfile.dispatch:63-66, docs/dispatch-runtime-operator.md:74). Het Codex-equivalent /sources/codex_api_key werkt op dezelfde manier (dispatch-child.ts:15-17).
  • Uit de payload gehouden: listSourceArtifacts filtert beide credentials uit source_artifacts (dispatch-child.ts:43). Ze komen alleen in de env van het kindproces (CLAUDE_CODE_OAUTH_TOKEN / CODEX_API_KEY, :91-95), en uitvoer wordt gescrubd (:165).
  • Maar wel leesbaar voor het model: het claude-proces krijgt --add-dir /sources (dispatch-child.ts:81). Daarmee mag het model met zijn bestandstools alles in /sources lezen, inclusief het credentialbestand.

Risico. Een prompt-injectie in een gereviewd document (inmiddels 18 producten, ISS-11) kan het model het token laten lezen en proberen het naar buiten te brengen. Bestaande verdediging:

  • egress is beperkt tot de provider-allowlist (egress-proxy);
  • het artefact wordt gescrubd;
  • het token staat al in de env van het proces, dus een model met shelltoegang kan het toch zien.

Het bestand binnen het --add-dir-pad maakt het lezen wel triviaal, zonder shell en met de gewone Read-tool. Er is geen aanwijzing dat het token is gelekt.

Mogelijke richtingen (nog niet gekozen).

  1. Laat de child-launcher het credentialbestand lezen en daarna verwijderen of onleesbaar maken, vóór het claude- of codex-proces start.
  2. Mount credentials buiten /sources, in een pad dat niet in --add-dir zit en niet leesbaar is voor het modelproces.
  3. Geef --add-dir alleen de echte bron-artefacten mee (een aparte directory zonder operatorbronnen).

Controleer bij elke richting ook codex_api_key en de Codex-sandbox (--cd/sandboxpaden).

Niet gedaan: geen wijziging, geen tokenrotatie. Of rotatie nodig is, is aan JP; er is geen bewijs van uitlekken.

Onderzoek


2026-10-04T22:40:46.891Z — mac:claude

Analyse (code + Claude Code-docs; nog niet live getest)

Kern: het bestand in --add-dir /sources is niet het eigenlijke lek. Het model kan het token via minstens drie wegen lezen, en richtingen 1–3 uit de beschrijving dichten daar geen enkele van.

Leeswegen voor het model

Weg Werkt? Waarom
Read /sources/claude_oauth_token ja Staat in --add-dir (lib/dispatch-child.ts:81). Ook zonder add-dir: volgens de docs (permission-modes, "first read outside the working directories") worden reads buiten de werkmappen in -p-runs gewoon uitgevoerd, tenzij permissions.blockReadsOutsideWorkingDirectories aan staat.
Bash: cat /sources/claude_oauth_token ja Bash staat in --allowedTools. Launcher en model draaien als dezelfde uid 10001, en het bestand moet leesbaar zijn voor 10001.
Bash: env / /proc/<pid>/environ ja CLAUDE_CODE_OAUTH_TOKEN staat in de env van claude (dispatch-child.ts:95). Subprocessen erven die, omdat CLAUDE_CODE_SUBPROCESS_ENV_SCRUB niet gezet is.

Waarom richtingen 1–3 niet werken

  1. Verwijderen of onleesbaar maken na lezen: onmogelijk. /sources is een readonly bind-mount (lib/dispatch-runtime.ts:68); dezelfde uid, met --cap-drop ALL en no-new-privileges. Chmod, unlink en uid-wissel kunnen dus geen van alle.
  2. Buiten /sources mounten: Read in -p-modus en Bash cat lezen ook daarbuiten.
  3. Een aparte add-dir met alleen de artefacten: zelfde reden als 2.

Het echte exfiltratiepad

Egress is beperkt tot api.anthropic.com, dus rechtstreeks naar buiten sturen kan niet. Er blijft één weg over: het rapport zelf (result/transcript). redactChildText scrubt alleen de exacte waarde en bekende tokenvormen. Gecodeerde varianten (base64, omgekeerd, in stukjes) komen erlangs. Wie een gereviewd document schrijft en het reviewrapport kan lezen, kan het token zo binnenhalen.

Voor Codex staat dit al als geaccepteerd restrisico in docs/dispatch-runtime-operator.md:57 ("scrubbing is the only barrier"). Het Claude-deel zegt alleen "same rules" en neemt het restrisico niet expliciet over, terwijl een setup-token aan het abonnement hangt en lang geldig is.

Richtingen die wél iets doen

  • A. Credential-injecting gateway (structureel). Het kind krijgt nooit een token. ANTHROPIC_BASE_URL gaat naar een lokale endpoint op de egress-bridge of -proxy, die de auth toevoegt. Dit lost Claude en Codex allebei op. Het is het meeste werk: de proxy doet nu alleen CONNECT en moet een HTTP-endpoint met eigen TLS naar de provider krijgen.
  • B. Goedkope gelaagde hardening:
    • CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 in buildClaudeEnv: Bash ziet het token dan niet meer in de env. Op Linux hoort de scrub ook een aparte PID-namespace te geven; of die start onder cap-drop ALL moet blijken.
    • Deny-regels Read(//sources/claude_oauth_token) en Read(//sources/codex_api_key) via settings of --disallowedTools. Deny gaat voor allow, ook via symlinks.
    • Review-slots zonder Bash. Read/Glob/Grep volstaan voor een review. Dit sluit cat en env in één keer af.
    • Restgat: een taak-child met Bash die zelf cat doet op het bestand. Dat dicht alleen A.
  • C. Documenteren. Neem het restrisico voor de Claude-child expliciet op in de operator-docs.

Advies: B nu voor review-slots, A als vervolg.

Nog te verifiëren (proef in model-claude-probe)

  1. Slaagt Read buiten de add-dir echt in -p met --allowedTools Read?
  2. Start de PID-namespace van CLAUDE_CODE_SUBPROCESS_ENV_SCRUB onder --cap-drop ALL / no-new-privileges, of faalt Bash dan?

Oplossing

Nog geen oplossing.

> Beheerd door Scrum4Me — wijzigingen hier worden overschreven. Bron: https://thuis.jp-visser.nl/issues/cmuubx4p80004j217woxfjmxl Status: investigating · Severity: s3_major · Gemeld door: mac:claude · Occurrences: 1 (laatst: 2026-10-04T21:24:46.988Z) · Aangemaakt: 2026-10-04T21:24:46.988Z ## Registratie **Waarneming (2026-10-04, ISS-11-acceptatie).** De Claude-dispatchreviewer (request `8125d760`, job `a6616e02`, slot `job:managed:srv-review-claude-1`) meldde in zijn rapport een bestand `/sources/claude_oauth_token` dat niet in `source_artifacts` staat. Hij heeft het bewust niet gelezen. **Code (scrum4me-docker origin/master).** - **Waar het token staat:** het OAuth-token komt als operatorbron binnen in `/sources/claude_oauth_token` (`lib/dispatch-child.ts:18-20`, `Dockerfile.dispatch:63-66`, `docs/dispatch-runtime-operator.md:74`). Het Codex-equivalent `/sources/codex_api_key` werkt op dezelfde manier (`dispatch-child.ts:15-17`). - **Uit de payload gehouden:** `listSourceArtifacts` filtert beide credentials uit `source_artifacts` (`dispatch-child.ts:43`). Ze komen alleen in de env van het kindproces (`CLAUDE_CODE_OAUTH_TOKEN` / `CODEX_API_KEY`, `:91-95`), en uitvoer wordt gescrubd (`:165`). - **Maar wel leesbaar voor het model:** het claude-proces krijgt `--add-dir /sources` (`dispatch-child.ts:81`). Daarmee mag het model met zijn bestandstools alles in `/sources` lezen, inclusief het credentialbestand. **Risico.** Een prompt-injectie in een gereviewd document (inmiddels 18 producten, ISS-11) kan het model het token laten lezen en proberen het naar buiten te brengen. Bestaande verdediging: - egress is beperkt tot de provider-allowlist (egress-proxy); - het artefact wordt gescrubd; - het token staat al in de env van het proces, dus een model met shelltoegang kan het toch zien. Het bestand binnen het `--add-dir`-pad maakt het lezen wel triviaal, zonder shell en met de gewone Read-tool. Er is geen aanwijzing dat het token is gelekt. **Mogelijke richtingen (nog niet gekozen).** 1. Laat de child-launcher het credentialbestand lezen en daarna verwijderen of onleesbaar maken, vóór het claude- of codex-proces start. 2. Mount credentials buiten `/sources`, in een pad dat niet in `--add-dir` zit en niet leesbaar is voor het modelproces. 3. Geef `--add-dir` alleen de echte bron-artefacten mee (een aparte directory zonder operatorbronnen). Controleer bij elke richting ook `codex_api_key` en de Codex-sandbox (`--cd`/sandboxpaden). **Niet gedaan:** geen wijziging, geen tokenrotatie. Of rotatie nodig is, is aan JP; er is geen bewijs van uitlekken. ## Onderzoek --- *2026-10-04T22:40:46.891Z — mac:claude* ## Analyse (code + Claude Code-docs; nog niet live getest) **Kern:** het bestand in `--add-dir /sources` is niet het eigenlijke lek. Het model kan het token via minstens drie wegen lezen, en richtingen 1–3 uit de beschrijving dichten daar geen enkele van. ### Leeswegen voor het model | Weg | Werkt? | Waarom | |---|---|---| | `Read /sources/claude_oauth_token` | ja | Staat in `--add-dir` (`lib/dispatch-child.ts:81`). Ook **zonder** add-dir: volgens de docs (permission-modes, "first read outside the working directories") worden reads buiten de werkmappen in `-p`-runs gewoon uitgevoerd, tenzij `permissions.blockReadsOutsideWorkingDirectories` aan staat. | | `Bash: cat /sources/claude_oauth_token` | ja | `Bash` staat in `--allowedTools`. Launcher en model draaien als dezelfde uid 10001, en het bestand moet leesbaar zijn voor 10001. | | `Bash: env` / `/proc/<pid>/environ` | ja | `CLAUDE_CODE_OAUTH_TOKEN` staat in de env van claude (`dispatch-child.ts:95`). Subprocessen erven die, omdat `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB` niet gezet is. | ### Waarom richtingen 1–3 niet werken 1. **Verwijderen of onleesbaar maken na lezen:** onmogelijk. `/sources` is een readonly bind-mount (`lib/dispatch-runtime.ts:68`); dezelfde uid, met `--cap-drop ALL` en `no-new-privileges`. Chmod, unlink en uid-wissel kunnen dus geen van alle. 2. **Buiten `/sources` mounten:** Read in `-p`-modus en `Bash cat` lezen ook daarbuiten. 3. **Een aparte add-dir met alleen de artefacten:** zelfde reden als 2. ### Het echte exfiltratiepad Egress is beperkt tot `api.anthropic.com`, dus rechtstreeks naar buiten sturen kan niet. Er blijft één weg over: het **rapport zelf** (result/transcript). `redactChildText` scrubt alleen de exacte waarde en bekende tokenvormen. Gecodeerde varianten (base64, omgekeerd, in stukjes) komen erlangs. Wie een gereviewd document schrijft en het reviewrapport kan lezen, kan het token zo binnenhalen. Voor Codex staat dit al als geaccepteerd restrisico in `docs/dispatch-runtime-operator.md:57` ("scrubbing is the only barrier"). Het Claude-deel zegt alleen "same rules" en neemt het restrisico niet expliciet over, terwijl een `setup-token` aan het abonnement hangt en lang geldig is. ### Richtingen die wél iets doen - **A. Credential-injecting gateway (structureel).** Het kind krijgt nooit een token. `ANTHROPIC_BASE_URL` gaat naar een lokale endpoint op de egress-bridge of -proxy, die de auth toevoegt. Dit lost Claude en Codex allebei op. Het is het meeste werk: de proxy doet nu alleen CONNECT en moet een HTTP-endpoint met eigen TLS naar de provider krijgen. - **B. Goedkope gelaagde hardening:** - `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1` in `buildClaudeEnv`: Bash ziet het token dan niet meer in de env. Op Linux hoort de scrub ook een aparte PID-namespace te geven; of die start onder `cap-drop ALL` moet blijken. - Deny-regels `Read(//sources/claude_oauth_token)` en `Read(//sources/codex_api_key)` via settings of `--disallowedTools`. Deny gaat voor allow, ook via symlinks. - **Review-slots zonder `Bash`.** Read/Glob/Grep volstaan voor een review. Dit sluit `cat` en `env` in één keer af. - Restgat: een taak-child met Bash die zelf `cat` doet op het bestand. Dat dicht alleen A. - **C. Documenteren.** Neem het restrisico voor de Claude-child expliciet op in de operator-docs. **Advies:** B nu voor review-slots, A als vervolg. ### Nog te verifiëren (proef in `model-claude-probe`) 1. Slaagt `Read` buiten de add-dir echt in `-p` met `--allowedTools Read`? 2. Start de PID-namespace van `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB` onder `--cap-drop ALL` / `no-new-privileges`, of faalt Bash dan? ## Oplossing _Nog geen oplossing._ <!-- s4m:issue:cmuubx4p80004j217woxfjmxl -->
Sign in to join this conversation.
No labels
severity/s3
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
janpeter/scrum4me-docker#108
No description provided.