feat: codex/gemini runners spawn hermetic children

Same allowlist scrub as the claude runners, with each provider's auth
surface re-admitted via extraAllow (codex: OPENAI_API_KEY/CODEX_* plus
its tempHome .codex copy; gemini: GEMINI_*/GOOGLE_* with real HOME for
~/.gemini auth). The gemini spawn previously inherited the full operator
env with no env property at all.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-12 11:18:22 -07:00
parent 9799593abb
commit b89ce2677c
2 changed files with 16 additions and 6 deletions
+9 -5
View File
@@ -15,6 +15,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { hermeticChildEnv } from './hermetic-env';
// --- Interfaces ---
@@ -201,15 +202,18 @@ export async function runCodexSkill(opts: {
// Build codex exec command
const args = ['exec', prompt, '--json', '-s', sandbox];
// Spawn codex with temp HOME so it discovers our installed skill
// Spawn codex with temp HOME so it discovers our installed skill.
// Hermetic scrub (test/helpers/hermetic-env.ts) with codex's auth surface
// re-admitted: codex auths from $HOME/.codex (copied into tempHome above)
// plus OPENAI_API_KEY/CODEX_* when present. HOME override merges last.
const proc = Bun.spawn(['codex', ...args], {
cwd: cwd || skillDir,
stdout: 'pipe',
stderr: 'pipe',
env: {
...process.env,
HOME: tempHome,
},
env: hermeticChildEnv(
{ HOME: tempHome },
{ extraAllow: ['OPENAI_API_KEY', 'CODEX_*'] },
),
});
// Race against timeout
+7 -1
View File
@@ -14,6 +14,7 @@
*/
import * as path from 'path';
import { hermeticChildEnv } from './hermetic-env';
// --- Interfaces ---
@@ -122,11 +123,16 @@ export async function runGeminiSkill(opts: {
// Build gemini command
const args = ['-p', prompt, '--output-format', 'stream-json', '--yolo'];
// Spawn gemini — uses real HOME for auth, cwd for skill discovery
// Spawn gemini — uses real HOME for auth (~/.gemini; HOME is allowlisted),
// cwd for skill discovery. Hermetic scrub with gemini's auth surface
// re-admitted (previously this spawn inherited the full operator env).
const proc = Bun.spawn(['gemini', ...args], {
cwd: cwd || process.cwd(),
stdout: 'pipe',
stderr: 'pipe',
env: hermeticChildEnv(undefined, {
extraAllow: ['GEMINI_API_KEY', 'GOOGLE_API_KEY', 'GOOGLE_APPLICATION_CREDENTIALS', 'GOOGLE_CLOUD_*', 'GEMINI_*'],
}),
});
// Race against timeout